Written by Aunoy Poddar July 9th, 2022

Process the puncta quantified raw data

current_file <- rstudioapi::getActiveDocumentContext()$path
output_file <- stringr::str_replace(current_file, '.Rmd', '.R')
knitr::purl(current_file, output = output_file)
file.edit(output_file)

Import packages and functions

library(Seurat)
Warning message:
In fun(libname, pkgname) : couldn't connect to display ":0"
library(tictoc)
library(ggplot2)
library(patchwork)
library(pheatmap)
library(RColorBrewer)
library(tidyverse)
library(gridExtra)
library(png)
library(cowplot)
library(magick)

Load the data

data_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/rethresholded'
meta_dir = '/home/aunoy/st/arc_profiling/st_analysis/hand_annotated_data/overlay'
output_dir_plot = '/home/aunoy/st/arc_profiling/st_analysis/results/plots'
output_dir_tbls = '/home/aunoy/st/arc_profiling/st_analysis/results/tables'

Merge both datasets and generate a metadata column that corresponds

to the cell

df_408 = data.frame()
for (file_name in list.files(data_dir)){
  print(file_name)
  if(grepl('164', file_name)){
    next
  }
  #if(grepl('408_TC', file_name) | grepl('408_vMS', file_name)){
  #  next
  #}
  df_to_append <- read.table(file.path(data_dir, file_name), sep = ',', header = TRUE)
  while(length(ind <- which(df_to_append$Image.Name == "")) > 0){
    df_to_append$Image.Name[ind] <- df_to_append$Image.Name[ind -1]
  }
  
  colnames(df_to_append) <- toupper(colnames(df_to_append))
  df_to_append <- df_to_append %>%
    mutate(area = strsplit(file_name, '.csv')[[1]])
  
  ## Add relative_XY_position
  
  if(!is_empty(df_408)){
    df_to_append <- df_to_append %>%
          dplyr::select(colnames(df_408))
  }
  df_408 <- rbind(df_408, df_to_append)
}
[1] "164_CC.csv"
[1] "164_MS_CC.csv"
[1] "164_MS_TC.csv"
[1] "164_TC.csv"
[1] "408_CC.csv"
[1] "408_dMS_TC.csv"
[1] "408_MS_CC.csv"
[1] "408_TC.csv"
[1] "408_vMS_TC.csv"
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='_Cluster', replacement=''))
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='[*]', replacement=''))
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='X', replacement=''))
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='L2_', replacement='L2-'))
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='-L2', replacement='_L2'))
df_408$IMAGE.NAME = unlist(lapply(df_408$IMAGE.NAME, gsub, pattern='Tc_12', replacement='TC_12'))
## Missing
df_408 = df_408[df_408$IMAGE.NAME != 'Layer1', ]
df_408 = df_408[df_408$IMAGE.NAME != 'TC_1', ]
df_408 = df_408[df_408$IMAGE.NAME != 'TC_18', ]
df_408 = df_408[df_408$IMAGE.NAME != 'TC_19', ]
#df_408$IMAGE.NAME = toupper(df_408$IMAGE.NAME)
unique(df_408$IMAGE.NAME)
 [1] "CC_Cortical1" "CC_Cortical2" "CC_L2-1"      "CC_L2-2"      "CC_L2-3"      "TC_2"        
 [7] "TC_3"         "TC_4"         "TC_5"         "TC_6"         "TC_7"         "TC_8"        
[13] "TC_9"         "TC_10"        "CC_4"         "CC_5"         "CC_6"         "CC_7"        
[19] "CC_8"         "CC_9"         "CC_10"        "CC_11"        "CC_12"        "TC_16"       
[25] "TC_17"        "TC_20"        "TC_11"        "TC_12"        "TC_13"        "TC_14"       
[31] "TC_15"       

Now we know that everything is equal to one another, we should load the variable

image_names = unique(df_408$IMAGE.NAME)
# Preset these variables to negative values so I can easily check if they were updated later
df_408$X = -1
df_408$Y = -1
# set some normalization variables
## This is the size of the image when the pixel values are taken from top left down
IMAGE_SIZE = 1024
## This is the size of an image in the global coordinate space
IMAGE_LEN = 20

# Load the dataframe with global and relative coordinates
img_cords = read.table(file.path(meta_dir, '408_pixel_coordinates.csv'), sep = ',', header = TRUE)

images = list.files(meta_dir)
for(image_name in image_names){
      if(grepl('164', image_name)){
      next
    }
    split_names = strsplit(image_name, '_')
    cortex = toupper(split_names[[1]][1])
    number = split_names[[1]][2]
    number_csv = paste0('_', number, '.csv')
    filename = images[grepl(cortex, images) & grepl(number_csv, images) & grepl('408', images)]
    coordinates = read.table(file.path(meta_dir, filename), sep = ',', header = TRUE)
    ## checked already that lists are equal, missing 1, 18, 19 for now, layer 1 and others
    if(cortex == 'CC'){ 
      #print(paste('cc', filename, image_name))
      x_adj = 0
      y_adj = 0
    } else if(as.numeric(number) <= 10){
      #print(paste('tc<11', filename, image_name))
      x_adj = 180#img_cords[img_cords$Name == 'G_TC_1', 'x']
      y_adj = 210 #img_cords[img_cords$Name == 'G_TC_1', 'y']
    }else{
      #print(paste('tc>=11', filename, image_name))
      x_adj = 380#img_cords[img_cords$Name == 'G_TC_11', 'x']
      y_adj = 410 #img_cords[img_cords$Name == 'G_TC_11', 'y']
    }
    
    ## so this is a little tricky, so need to get it right
    ## Remember, it is the top right that the coordinate is coming from, but
    ## the bottom right is the new coordinate space.
    ## so first when we get the original coordinate space, to set to relative
    ## of bottom would be the same X, but 1024 - Y
    
    ## push out the coordinates for better visualization
    x_repelled <- (512 - coordinates$X_Coordinate_In_pixels)
    
    
    df_408[df_408$IMAGE.NAME == image_name, 'X'] = (x_repelled / 
                                                      IMAGE_SIZE * IMAGE_LEN) + 
                                                    img_cords[img_cords$Name == image_name, 'x'] + x_adj
    df_408[df_408$IMAGE.NAME == image_name, 'Y'] = ((1024-coordinates$Y_Coordinate_In_pixels) / 
                                                      IMAGE_SIZE * IMAGE_LEN) + 
                                                    img_cords[img_cords$Name == image_name, 'y'] + y_adj    
}

We have the coordinates for 408_TC and others

jy_408 = df_408 %>%
  dplyr::select(-c(area, IMAGE.NAME, X, Y)) %>%
  t() %>%
  CreateSeuratObject()

just set everything from below 1 in ratio to zero

jy_408 <- NormalizeData(jy_408, scale.factor = 1e5) ###
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
normed = GetAssayData(jy_408, slot = 'data')
normed[normed < 3] = 0
jy_408 <- SetAssayData(jy_408, slot = 'data', normed)
xycords = df_408 %>% select(c('X', 'Y')) %>% as.matrix()
colnames(xycords) <- c('pixel_1', 'pixel_2')

jy_408[["XY"]] <- CreateDimReducObject(embeddings = xycords, key = "pixel_", assay = DefaultAssay(jy_408))
jy_408$gad1_true = normed['GAD1',] != 0 & normed['SATB1',] == 0
Error in intI(i, n = x@Dim[1], dn[[1]], give.dn = FALSE) : 
  invalid character indexing
nkx21 <- normed['NKX2.1',] != 0 & normed['LHX6',] == 0 & jy_408$gad1_true 
lhx6 <- normed['NKX2.1',] == 0 & normed['LHX6',] != 0 & jy_408$gad1_true 
mge_both <- normed['NKX2.1',] != 0 & normed['LHX6',] != 0 & jy_408$gad1_true 
mge_neither <- normed['NKX2.1',] == 0 & normed['LHX6',] == 0 & jy_408$gad1_true 
non_interneuron <- !jy_408$gad1_true 

jy_408$mge_lineage = 'error'
jy_408$mge_lineage[nkx21] = 'NKX2.1'
jy_408$mge_lineage[lhx6] = 'LHX6'
jy_408$mge_lineage[mge_both] = 'NKX2.1 & LHX6'
jy_408$mge_lineage[mge_neither] = 'Non-MGE'
jy_408$mge_lineage[non_interneuron] = 'Non-IN'
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        cols = c('grey', 'purple'), reduction = "XY", pt.size = 0.2, group.by = 'gad1_true', order = which(jy_408$gad1_true))

DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 0.1, split.by = 'mge_lineage', 
        cells.highlight = list(nkx2.1 = which(nkx21), lhx6 = which(lhx6), both = which(mge_both)),
        cols.highlight = c('orange1','hotpink1', 'black'), 
        order = which(jy_408$gad1_true)) + scale_y_reverse() + scale_x_reverse() + NoAxes()

sp8 <- normed['SP8',] != 0 & normed['COUPTF2',] == 0 & jy_408$gad1_true 
couptf2 <- normed['SP8',] == 0 & normed['COUPTF2',] != 0 & jy_408$gad1_true 
cge_lge <- normed['SP8',] != 0 & normed['COUPTF2',] != 0 & jy_408$gad1_true 
neither_cge_lge <- normed['SP8',] == 0 & normed['COUPTF2',] == 0 & jy_408$gad1_true 
non_interneuron <- !jy_408$gad1_true 

jy_408$cge_lineage = 'error'
jy_408$cge_lineage[sp8] = 'SP8'
jy_408$cge_lineage[couptf2] = 'COUPTF2'
jy_408$cge_lineage[cge_lge] = 'SP8 & COUPTF2'
jy_408$cge_lineage[neither_cge_lge] = 'Non-CGE/LGE'
jy_408$cge_lineage[non_interneuron] = 'Non-IN'
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, split.by = 'cge_lineage', 
        cells.highlight = list(sp8 = which(sp8), couptf2 = which(couptf2), both = which(cge_lge)),
        cols.highlight = c('red','blue', 'purple'), 
        order = which(jy_408$gad1_true)) + scale_y_reverse() + scale_x_reverse() + NoAxes()

DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('greenyellow', 'lightgoldenrodyellow', 'orange1', 'aquamarine4', 'tomato', 'dodgerblue3', 'violetred3'), 
        cols = c('greenyellow', 'lightgoldenrodyellow', 'aquamarine4', 'orange1', 'dodgerblue3', 'tomato', 'violetred3'), 
        reduction = "XY", order = c(6, 4, 3, 2, 0, 1, 5), pt.size = 1) + scale_y_reverse() + scale_x_reverse() #group.by = 'mge_lineage', 

        #cells.highlight = list(sp8 = which(sp8), couptf2 = which(couptf2), both = which(cge_lge)),
        #cols.highlight = c('red','blue', 'purple'), 
        #order = which(jy_408$gad1_true)) + scale_y_reverse() + scale_x_reverse()
cluster_by_loc = as.data.frame(as.matrix(cbind(rownames(df_408), jy_408$seurat_clusters, df_408$X, df_408$Y)))
colnames(cluster_by_loc) = c('cellnum', 'cluster', 'X', 'Y')
cluster_by_loc %>%
  #filter(cluster == 3) %>%
  ggplot(aes(x = X, color = cluster)) + geom_histogram(position = 'identity', bins = 50, binwidth = 1000, stat = 'count')
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster0 = which(jy_408$seurat_clusters == 0))) + scale_y_reverse() + scale_x_reverse()#,
        #cols.highlight = c('red','blue', 'purple'), 
        #order = which(jy_408$gad1_true)) + scale_y_reverse() + scale_x_reverse()
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster1 = which(jy_408$seurat_clusters == 1))) + scale_y_reverse() + scale_x_reverse()#,
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster2 = which(jy_408$seurat_clusters == 2))) + scale_y_reverse() + scale_x_reverse()#,
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster3 = which(jy_408$seurat_clusters == 3))) + scale_y_reverse() + scale_x_reverse()#,
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster4 = which(jy_408$seurat_clusters == 4))) + scale_y_reverse() + scale_x_reverse()#,
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster5 = which(jy_408$seurat_clusters == 5))) + scale_y_reverse() + scale_x_reverse()#,
DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1, #group.by = 'mge_lineage', 
        cells.highlight = list(cluster6 = which(jy_408$seurat_clusters == 6))) + scale_y_reverse() + scale_x_reverse()#,
FeaturePlot(jy_408, features = c('NKX2.1', 'LHX6'),
        reduction = "XY", pt.size = 1, order = TRUE, split.by = 'area', by.col = TRUE) + scale_y_reverse() + scale_x_reverse() + NoAxes() + NoLegend()
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        cols = c('greenyellow', 'lightgoldenrodyellow', 'orange1', 'aquamarine4', 'tomato', 'dodgerblue3', 'violetred3'),
        reduction = "XY", pt.size = 1, split.by = 'seurat_clusters') + scale_y_reverse() + scale_x_reverse() + NoAxes() + NoLegend()

jy_408.markers <- FindAllMarkers(jy_408, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
Calculating cluster 0

  |                                                  | 0 % ~calculating  
  |+++++++++                                         | 17% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++        | 83% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 1

  |                                                  | 0 % ~calculating  
  |+++++                                             | 9 % ~00s          
  |++++++++++                                        | 18% ~00s          
  |++++++++++++++                                    | 27% ~00s          
  |+++++++++++++++++++                               | 36% ~00s          
  |+++++++++++++++++++++++                           | 45% ~00s          
  |++++++++++++++++++++++++++++                      | 55% ~00s          
  |++++++++++++++++++++++++++++++++                  | 64% ~00s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 2

  |                                                  | 0 % ~calculating  
  |+++++                                             | 10% ~00s          
  |++++++++++                                        | 20% ~00s          
  |+++++++++++++++                                   | 30% ~00s          
  |++++++++++++++++++++                              | 40% ~00s          
  |+++++++++++++++++++++++++                         | 50% ~00s          
  |++++++++++++++++++++++++++++++                    | 60% ~00s          
  |+++++++++++++++++++++++++++++++++++               | 70% ~00s          
  |++++++++++++++++++++++++++++++++++++++++          | 80% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 90% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 3

  |                                                  | 0 % ~calculating  
  |++++++                                            | 11% ~00s          
  |++++++++++++                                      | 22% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++++                           | 44% ~00s          
  |++++++++++++++++++++++++++++                      | 56% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 4

  |                                                  | 0 % ~calculating  
  |++++                                              | 8 % ~00s          
  |++++++++                                          | 15% ~00s          
  |++++++++++++                                      | 23% ~00s          
  |++++++++++++++++                                  | 31% ~00s          
  |++++++++++++++++++++                              | 38% ~00s          
  |++++++++++++++++++++++++                          | 46% ~00s          
  |+++++++++++++++++++++++++++                       | 54% ~00s          
  |+++++++++++++++++++++++++++++++                   | 62% ~00s          
  |+++++++++++++++++++++++++++++++++++               | 69% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 77% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++       | 85% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++++   | 92% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 5

  |                                                  | 0 % ~calculating  
  |+++++                                             | 9 % ~00s          
  |++++++++++                                        | 18% ~00s          
  |++++++++++++++                                    | 27% ~00s          
  |+++++++++++++++++++                               | 36% ~00s          
  |+++++++++++++++++++++++                           | 45% ~00s          
  |++++++++++++++++++++++++++++                      | 55% ~00s          
  |++++++++++++++++++++++++++++++++                  | 64% ~00s          
  |+++++++++++++++++++++++++++++++++++++             | 73% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++         | 82% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++    | 91% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
Calculating cluster 6

  |                                                  | 0 % ~calculating  
  |++++++                                            | 11% ~00s          
  |++++++++++++                                      | 22% ~00s          
  |+++++++++++++++++                                 | 33% ~00s          
  |+++++++++++++++++++++++                           | 44% ~00s          
  |++++++++++++++++++++++++++++                      | 56% ~00s          
  |++++++++++++++++++++++++++++++++++                | 67% ~00s          
  |+++++++++++++++++++++++++++++++++++++++           | 78% ~00s          
  |+++++++++++++++++++++++++++++++++++++++++++++     | 89% ~00s          
  |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed=00s  
jy_408.markers %>%
   group_by(cluster) %>%
   slice_max(n = 32, order_by = avg_log2FC)

SATB2 neurons removed

jy_408_IN <- jy_408[, jy_408$gad1_true]
jy_408_IN <- FindVariableFeatures(jy_408_IN, selection.method = "vst")
all.genes <- rownames(jy_408_IN)
jy_408_IN <- ScaleData(jy_408_IN, features = all.genes)
jy_408_IN <- RunPCA(jy_408_IN, approx = FALSE)
jy_408_IN <- FindNeighbors(jy_408_IN, dims = 1:30)
jy_408_IN <- FindClusters(jy_408_IN, resolution = 0.8)
jy_408_IN <- RunUMAP(jy_408_IN, dims = 1:30)

DimPlot(jy_408_IN, reduction = "umap", group.by = 'seurat_clusters')
jy_408_IN[["XY"]] <- CreateDimReducObject(embeddings = xycords[jy_408$gad1_true, ], key = "pixel_", assay = DefaultAssay(jy_408_IN))
DimPlot(jy_408_IN, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1) + scale_y_reverse() + scale_x_reverse() 
jy_408_IN.markers <- FindAllMarkers(jy_408_IN, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
jy_408_IN.markers %>%
   group_by(cluster) %>%
   slice_max(n = 32, order_by = avg_log2FC)

No SATB2 neurons removed

jy_408_IN <- jy_408[, jy_408$gad1_true]
jy_408_IN <- FindVariableFeatures(jy_408_IN, selection.method = "vst")
all.genes <- rownames(jy_408_IN)
jy_408_IN <- ScaleData(jy_408_IN, features = all.genes)
jy_408_IN <- RunPCA(jy_408_IN, approx = FALSE)
jy_408_IN <- FindNeighbors(jy_408_IN, dims = 1:30)
jy_408_IN <- FindClusters(jy_408_IN, resolution = 0.8)
jy_408_IN <- RunUMAP(jy_408_IN, dims = 1:30)

DimPlot(jy_408_IN, reduction = "umap", group.by = 'seurat_clusters')
jy_408_IN[["XY"]] <- CreateDimReducObject(embeddings = xycords[jy_408$gad1_true, ], key = "pixel_", assay = DefaultAssay(jy_408_IN))
DimPlot(jy_408_IN, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 1) + scale_y_reverse() + scale_x_reverse() 
jy_408_IN.markers <- FindAllMarkers(jy_408_IN, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
jy_408_IN.markers %>%
   group_by(cluster) %>%
   slice_max(n = 32, order_by = avg_log2FC)

Adjust to get the right overlay

image_names = unique(df_408$IMAGE.NAME)
# Preset these variables to negative values so I can easily check if they were updated later
df_408$X = -1
df_408$Y = -1
# set some normalization variables
## This is the size of the image when the pixel values are taken from top left down
IMAGE_SIZE = 1024
## This is the size of an image in the global coordinate space
IMAGE_LEN = 20

# Load the dataframe with global and relative coordinates
img_cords = read.table(file.path(meta_dir, '408_pixel_coordinates.csv'), sep = ',', header = TRUE)

images = list.files(meta_dir)
for(image_name in image_names){
    split_names = strsplit(image_name, '_')
    cortex = toupper(split_names[[1]][1])
    number = split_names[[1]][2]
    number_csv = paste0('_', number, '.csv')
    filename = images[grepl(cortex, images) & grepl(number_csv, images)]
    coordinates = read.table(file.path(meta_dir, filename), sep = ',', header = TRUE)
    ## checked already that lists are equal, missing 1, 18, 19 for now, layer 1 and others
    if(cortex == 'CC'){ 
      #print(paste('cc', filename, image_name))
      x_adj = 20
      y_adj = 188
    } else if(as.numeric(number) <= 10){
      #print(paste('tc<11', filename, image_name))
      x_adj = 410#img_cords[img_cords$Name == 'G_TC_1', 'x']
      y_adj = 470 #img_cords[img_cords$Name == 'G_TC_1', 'y']
    }else{
      #print(paste('tc>=11', filename, image_name))
      x_adj = 590#img_cords[img_cords$Name == 'G_TC_11', 'x']
      y_adj = 790 #img_cords[img_cords$Name == 'G_TC_11', 'y']
    }
    
    ## so this is a little tricky, so need to get it right
    ## Remember, it is the top right that the coordinate is coming from, but
    ## the bottom right is the new coordinate space.
    ## so first when we get the original coordinate space, to set to relative
    ## of bottom would be the same X, but 1024 - Y
    
    ## push out the coordinates for better visualization
    #x_repelled <- (512 - coordinates$X_Coordinate_In_pixels)
    
    
    df_408[df_408$IMAGE.NAME == image_name, 'X'] = (coordinates$X_Coordinate_In_pixels / 
                                                      IMAGE_SIZE * IMAGE_LEN) + 
                                                    img_cords[img_cords$Name == image_name, 'x']/1.5 + x_adj
    df_408[df_408$IMAGE.NAME == image_name, 'Y'] = ((1024-coordinates$Y_Coordinate_In_pixels) / 
                                                      IMAGE_SIZE * IMAGE_LEN) + 
                                                    img_cords[img_cords$Name == image_name, 'y'] + y_adj    
}

xycords = df_408 %>% select(c('X', 'Y')) %>% as.matrix()
colnames(xycords) <- c('pixel_1', 'pixel_2')

jy_408[["XY"]] <- CreateDimReducObject(embeddings = xycords, key = "pixel_", assay = DefaultAssay(jy_408))
#https://stackoverflow.com/questions/9917049/inserting-an-image-to-ggplot2

theme_set(theme_cowplot())

bad_colors <- DimPlot(jy_408, cols = c('greenyellow', 'lightgoldenrodyellow', 'aquamarine4', 'orange1', 'dodgerblue3', 'tomato', 'violetred3'),  reduction = "XY", pt.size = 0.01, order = c(6, 4, 3, 2, 0, 1, 5)) + scale_y_reverse() + scale_x_reverse() + xlim(852, 0) + ylim(1242, 0) + NoLegend() + coord_fixed() + NoAxes()
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
#xorig = -852
#yorig = -1242

ggdraw() +
  draw_image('~/st/arc_profiling/st_analysis/hand_annotated_data/images/408_slice_noboxes_nocolor.png',
             x = 0, y = 0) +
  draw_plot(bad_colors)

DimPlot(jy_408, reduction = "XY", pt.size = 0.1)+ scale_y_reverse() + scale_x_reverse()   + xlim(852, 0) + ylim(1242, 0) # + NoLegend() + NoAxes()
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.

jy_408_sp[["XY"]] <- CreateDimReducObject(embeddings = xycords, key = "pixel_", assay = DefaultAssay(jy_408_sp))

reln <- normed['VLDLR',] != 0 & normed['LRP8',] == 0 & jy_408$gad1_true 
lrp8 <- normed['VLDLR',] == 0 & normed['LRP8',] != 0 & jy_408$gad1_true 
relnboth <- normed['VLDLR',] != 0 & normed['LRP8',] != 0 & jy_408$gad1_true 
relnneither <- normed['VLDLR',] == 0 & normed['LRP8',] == 0 & jy_408$gad1_true 
non_interneuron <- !jy_408$gad1_true 

jy_408$reln_signaling = 'error'
jy_408$reln_signaling[reln] = 'VLDLR'
jy_408$reln_signaling[lrp8] = 'LRP8'
jy_408$reln_signaling[relnboth] = 'VLDLR & LRP8'
jy_408$reln_signaling[relnneither] = 'Neither'
jy_408$reln_signaling[non_interneuron] = 'Non-IN'
## just define sets of cells that I want to plot
cingulate_MS = grepl('CC', df_408$IMAGE.NAME) 
dorsal_TC_MS = df_408$IMAGE.NAME %in% outer('TC_', 2:10, FUN=paste0)
ventral_TC_MS = !cingulate_MS & !dorsal_TC_MS
FeaturePlot(jy_408, cells = which(ventral_TC_MS), reduction = "XY", features = 'TBR1') + scale_y_reverse() + scale_x_reverse() 
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

FeaturePlot(jy_408, cells = which(ventral_TC_MS), reduction = "XY", features = 'LRP8') + scale_y_reverse() + scale_x_reverse() 
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

FeaturePlot(jy_408, cells = which(dorsal_TC_MS), reduction = "XY", features = c('CXCR4', 'CALB2'), order = TRUE) + scale_y_reverse() + scale_x_reverse() 
FeaturePlot(jy_408, cells = which(ventral_TC_MS), reduction = "XY", features = c('CXCR4', 'CALB2'), order = TRUE) + scale_y_reverse() + scale_x_reverse() 
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

FeaturePlot(jy_408, cells = which(cingulate_MS), reduction = "XY", features = c('NKX2.1'), order = TRUE) + scale_y_reverse() + scale_x_reverse() 
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

FeaturePlot(jy_408, cells = which(ventral_TC_MS), reduction = "XY", features = c('CXCR4', 'CALB2'), order = TRUE) +
  stat_density_2d(aes(fill = ..density..), geom = "raster", contour = FALSE) +
  scale_fill_distiller(palette=4, direction=-1) 
normed_data = t(as.matrix(GetAssayData(jy_408, slot = 'data'))) 
norm_bools = normed_data > 0

density_df = as.data.frame(cbind(xycords, norm_bools, jy_408$seurat_clusters))
colnames(density_df) = c('X', 'Y', colnames(norm_bools), 'cluster')
density_df$fov = 'error'
density_df$fov[dorsal_TC_MS] = 'dMS_TC'
density_df$fov[ventral_TC_MS] = 'vMS_TC'
density_df$fov[cingulate_MS] = 'MS_CC'
density_df %>%
  ggplot(aes(x = X, y = Y))  +
  geom_bin2d(bins = 20) +
  scale_fill_continuous(type = "viridis") +
  theme_bw()

DimPlot(jy_408, #cells = grepl('CC', df_408$area), 
        #cols = c('grey', 'purple', 'blue', 'red', 'hotpink1'), 
        reduction = "XY", pt.size = 0.1, split.by = 'reln_signaling', 
        cells.highlight = list(vldlr = which(reln), lrp8 = which(lrp8), both = which(relnboth)),
        cols.highlight = c('orange1','hotpink1', 'black'), 
        order = which(jy_408$gad1_true)) + scale_y_reverse() + scale_x_reverse()

LEts do the functional graphs

goi_primary = c('VIP', 'SST', 'CXCR4')
goi = c('VIP', 'SST', 'CXCR4', 'CXCR7', 'CXCL14', 'CXCL12', 'LRP8', 'VLDLR', 'RELN')

Let’s compare SP8 vs COUPTF2 average expression of these markers

fxn_genes  = as.data.frame(normed_data[, goi])
fxn_genes$cge = jy_408$cge_lineage
fxn_genes$fov = density_df$fov

fxn_genes %>%
  filter(fov == 'dMS_TC') %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = expr, fill = cge)) + geom_boxplot()# geom_bar(position="dodge", stat="identity")

fxn_genes %>%
  filter(fov == 'vMS_TC') %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = expr, fill = cge)) + geom_boxplot()# geom_bar(position="dodge", stat="identity")

cfov = 'MS_CC'
fxn_genes %>%
  filter(fov == cfov) %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = expr, fill = cge)) + geom_bar(position="dodge", stat="identity") +
  scale_fill_manual(values=c("green","gray","gray","blue", "red")) + ggtitle(cfov)

cfov = 'vMS_TC'
fxn_genes %>%
  filter(fov == cfov) %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = expr, fill = cge)) + geom_bar(position="dodge", stat="identity") +
  scale_fill_manual(values=c("green","gray","gray","blue", "red")) + ggtitle(cfov)

cfov = 'dMS_TC'
fxn_genes %>%
  filter(fov == cfov) %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = expr, fill = cge)) + geom_bar(position="dodge", stat="identity") +
  scale_fill_manual(values=c("green","gray","gray","blue", "red")) + ggtitle(cfov)

fxn_genes  = as.data.frame(norm_bools[, goi])
fxn_genes$cge = jy_408$cge_lineage
fxn_genes$fov = density_df$fov

fxn_genes %>%
  filter(fov == 'dMS_TC') %>%
  pivot_longer(!c(cge, fov), names_to = "gene", values_to = "expr") %>%
  ggplot(aes(x = gene, y = as.numeric(expr), fill = cge)) + geom_bar(position="dodge", stat="summary", fun = "mean") +
  xlab("pct") + ggtitle('dMS_TC')

fxn_genes  = as.data.frame(norm_bools[, goi])
fxn_genes$mge = jy_408$mge_lineage
fxn_genes$fov = density_df$fov

cfov = 'vMS_TC'

fxn_genes %>%
  filter(fov == cfov) %>%
  select(-fov) %>%
  pivot_longer(!mge, names_to = "gene", values_to = "expr") %>%
  data_summary(varname="expr", groupnames=c("gene", "mge")) %>%
  ggplot(aes(x=gene, y=expr, fill=mge)) + geom_bar(stat="identity", color="black", position=position_dodge())  + geom_errorbar(aes(ymin=expr-sem, ymax=expr+sem), width=.2, position=position_dodge(.9)) +
  ylab("pct") + ggtitle(cfov) + scale_fill_manual(values=c("cadetblue1","brown2","darkslateblue","grey", "khaki1")) +
  theme_classic()


#c("cadetblue1","brown2","darkslateblue","grey", "khaki1") for mge
#http://www.sthda.com/english/wiki/ggplot2-error-bars-quick-start-guide-r-software-and-data-visualization
library(plotrix)

Attaching package: ‘plotrix’

The following object is masked from ‘package:fields’:

    color.scale
data_summary <- function(data, varname, groupnames){
  require(plyr)
  summary_func <- function(x, col){
    c(mean = mean(x[[col]], na.rm=TRUE),
      sem = std.error(x[[col]], na.rm=TRUE))
  }
  data_sum<-ddply(data, groupnames, .fun=summary_func,
                  varname)
  data_sum <- rename(data_sum, c("mean" = varname))
 return(data_sum)
}
df2 <- fxn_genes %>%
  select(-fov) %>%
  pivot_longer(!cge, names_to = "gene", values_to = "expr") %>%
  data_summary(varname="expr", 
                    groupnames=c("gene", "cge"))
 ggplot(df2, aes(x=gene, y=expr, fill=cge)) + 
  geom_bar(stat="identity", color="black", position=position_dodge()) +
  geom_errorbar(aes(ymin=expr-sem, ymax=expr+sem), width=.2,
                 position=position_dodge(.9)) 

 ggplot(df2, aes(x=gene, y=expr, fill=cge)) + 
  geom_bar(stat="identity", color="black", position=position_dodge()) +
  geom_errorbar(aes(ymin=expr-sem, ymax=expr+sem), width=.2,
                 position=position_dodge(.9)) 

fxn_genes %>%
  select(-c(fov, mge)) %>%
  pivot_longer(!cge, names_to = "gene", values_to = "expr") %>%
  data_summary(varname="expr", groupnames=c("gene", "cge")) %>%
  ggplot(aes(x=gene, y=expr, fill=cge)) + geom_bar(stat="identity", color="black", position=position_dodge())  + geom_errorbar(aes(ymin=expr-sem, ymax=expr+sem), width=.2, position=position_dodge(.9)) +
  ylab("pct") + scale_fill_manual(values=c("darkolivegreen1","orange2","rosybrown4","gray96", "grey")) +
  theme_classic()

print(206/388)
[1] 0.5309278

Order the images

unique(df_408$IMAGE.NAME)
 [1] "CC_Cortical1" "CC_Cortical2" "CC_L2-1"      "CC_L2-2"      "CC_L2-3"      "TC_2"        
 [7] "TC_3"         "TC_4"         "TC_5"         "TC_6"         "TC_7"         "TC_8"        
[13] "TC_9"         "TC_10"        "CC_4"         "CC_5"         "CC_6"         "CC_7"        
[19] "CC_8"         "CC_9"         "CC_10"        "CC_11"        "CC_12"        "TC_16"       
[25] "TC_17"        "TC_20"        "TC_11"        "TC_12"        "TC_13"        "TC_14"       
[31] "TC_15"       
unique(df_408$IMAGE.NAME)
 [1] "CC_Cortical1" "CC_Cortical2" "CC_L2-1"      "CC_L2-2"      "CC_L2-3"      "TC_2"        
 [7] "TC_3"         "TC_4"         "TC_5"         "TC_6"         "TC_7"         "TC_8"        
[13] "TC_9"         "TC_10"        "CC_4"         "CC_5"         "CC_6"         "CC_7"        
[19] "CC_8"         "CC_9"         "CC_10"        "CC_11"        "CC_12"        "TC_16"       
[25] "TC_17"        "TC_20"        "TC_11"        "TC_12"        "TC_13"        "TC_14"       
[31] "TC_15"       
orderd = c('TC_20', 'TC_17', 'TC_16', 'TC_15', 'TC_14', 'TC_13', 'TC_12', 'TC_11',
           'TC_10', 'TC_9', 'TC_8', 'TC_7', 'TC_6', 'TC_4', 'TC_4', 'TC_3', 'TC_2',
           'CC_4', 'CC_5', 'CC_6', 'CC_6', 'CC_8', 'CC_9', 'CC_10', 'CC_11', 'CC_12',
           'CC_L2-1', 'CC_L2-2', 'CC_L2-3', 'CC_Cortical1', 'CC_Cortical2')
x_horz = 1:length(images_ordered) * 25
y_horz = rep(0, length(images_ordered))
horz_embedding = data.frame()
df_408$X_horz = -1
df_408$Y_horz = -1

images = list.files(meta_dir)
for(i in 1:length(images_ordered)){
    image_name = images_ordered[i]
    split_names = strsplit(image_name, '_')
    cortex = toupper(split_names[[1]][1])
    number = split_names[[1]][2]
    number_csv = paste0('_', number, '.csv')
    filename = images[grepl(cortex, images) & grepl(number_csv, images)]
    coordinates = read.table(file.path(meta_dir, filename), sep = ',', header = TRUE)
    ## checked already that lists are equal, missing 1, 18, 19 for now, layer 1 and others
 
    ## so this is a little tricky, so need to get it right
    ## Remember, it is the top right that the coordinate is coming from, but
    ## the bottom right is the new coordinate space.
    ## so first when we get the original coordinate space, to set to relative
    ## of bottom would be the same X, but 1024 - Y
    
    ## push out the coordinates for better visualization
    #x_repelled <- (512 - coordinates$X_Coordinate_In_pixels)
    
    
    df_408[df_408$IMAGE.NAME == image_name, 'X_horz'] = (coordinates$X_Coordinate_In_pixels / 
                                                      IMAGE_SIZE * IMAGE_LEN) + y_horz[i]
    df_408[df_408$IMAGE.NAME == image_name, 'Y_horz'] = ((1024-coordinates$Y_Coordinate_In_pixels) / 
                                                      IMAGE_SIZE * IMAGE_LEN) + x_horz[i]
}
hcoords = df_408 %>% dplyr::select(c('X_horz', 'Y_horz')) %>% as.matrix()
colnames(hcoords) <- c('pixel_1', 'pixel_2')

jy_408[["H"]] <- CreateDimReducObject(embeddings = hcoords, key = "pixel_", assay = DefaultAssay(jy_408))
Warning: Cannot add objects with duplicate keys (offending key: pixel_) setting key to original value 'h_'
jy_408$satb1_true = !jy_408$gad1_true
DimPlot(jy_408, cols = c('grey', 'purple'), reduction = "H", pt.size = 0.25, group.by = 'gad1_true', order = which(jy_408$gad1_true)) + coord_fixed(ratio = 0.5) + NoAxes() + NoLegend()

DimPlot(jy_408, reduction = "H", pt.size = 0.25, group.by = 'mge_lineage', order = "NKX2.1 & LHX6", cells.highlight = which(jy_408$mge_lineage == 'NKX2.1 & LHX6'), cols.highlight = 'red') + coord_fixed(ratio = 1) +  NoLegend() + NoAxes()

theme_Publication <- function(base_size=14, base_family="helvetica") {
      library(grid)
      library(ggthemes)
      (theme_foundation(base_size=base_size, base_family=base_family)
       + theme(plot.title = element_text(face = "bold",
                                         size = rel(1.2), hjust = 0.5),
               text = element_text(),
               panel.background = element_rect(colour = NA),
               plot.background = element_rect(colour = NA),
               panel.border = element_rect(colour = NA),
               axis.title = element_text(face = "bold",size = rel(1)),
               axis.title.y = element_text(angle=90,vjust =2),
               axis.title.x = element_text(vjust = -0.2),
               axis.text = element_text(), 
               axis.line = element_line(colour="black"),
               axis.ticks = element_line(),
               panel.grid.major = element_line(colour="#f0f0f0"),
               panel.grid.minor = element_blank(),
               legend.key = element_rect(colour = NA),
               legend.position = "bottom",
               legend.direction = "horizontal",
               legend.key.size= unit(0.2, "cm"),
               legend.margin = unit(0, "cm"),
               legend.title = element_text(face="italic"),
               plot.margin=unit(c(10,5,5,5),"mm"),
               strip.background=element_rect(colour="#f0f0f0",fill="#f0f0f0"),
               strip.text = element_text(face="bold")
          ))
      
}

scale_fill_Publication <- function(...){
      library(scales)
      discrete_scale("fill","Publication",manual_pal(values = c("#386cb0","#fdb462","#7fc97f","#ef3b2c","#662506","#a6cee3","#fb9a99","#984ea3","#ffff33")), ...)

}

scale_colour_Publication <- function(...){
      library(scales)
      discrete_scale("colour","Publication",manual_pal(values = c("#386cb0","#fdb462","#7fc97f","#ef3b2c","#662506","#a6cee3","#fb9a99","#984ea3","#ffff33")), ...)

}
FeaturePlot(jy_408, pt.size=0.1, reduction = "H", features = 'LRP8') + coord_fixed(ratio = 0.5) +  theme_Publication() + NoLegend() + NoAxes()
Warning: `legend.margin` must be specified using `margin()`. For the old behavior use legend.spacing

LS0tCnRpdGxlOiAic3RfcGh5c2ljYWwiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCldyaXR0ZW4gYnkgQXVub3kgUG9kZGFyCkp1bHkgOXRoLCAyMDIyCgojIFByb2Nlc3MgdGhlIHB1bmN0YSBxdWFudGlmaWVkIHJhdyBkYXRhCmBgYHtyIGV2YWw9RkFMU0V9CmN1cnJlbnRfZmlsZSA8LSByc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoCm91dHB1dF9maWxlIDwtIHN0cmluZ3I6OnN0cl9yZXBsYWNlKGN1cnJlbnRfZmlsZSwgJy5SbWQnLCAnLlInKQprbml0cjo6cHVybChjdXJyZW50X2ZpbGUsIG91dHB1dCA9IG91dHB1dF9maWxlKQpmaWxlLmVkaXQob3V0cHV0X2ZpbGUpCmBgYAoKIyMgSW1wb3J0IHBhY2thZ2VzIGFuZCBmdW5jdGlvbnMKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHRpY3RvYykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShwbmcpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShtYWdpY2spCgpgYGAKCiMjIExvYWQgdGhlIGRhdGEKYGBge3J9CmRhdGFfZGlyID0gJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvaGFuZF9hbm5vdGF0ZWRfZGF0YS9yZXRocmVzaG9sZGVkJwptZXRhX2RpciA9ICcvaG9tZS9hdW5veS9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvb3ZlcmxheScKb3V0cHV0X2Rpcl9wbG90ID0gJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvcmVzdWx0cy9wbG90cycKb3V0cHV0X2Rpcl90YmxzID0gJy9ob21lL2F1bm95L3N0L2FyY19wcm9maWxpbmcvc3RfYW5hbHlzaXMvcmVzdWx0cy90YWJsZXMnCmBgYAoKIyMjIE1lcmdlIGJvdGggZGF0YXNldHMgYW5kIGdlbmVyYXRlIGEgbWV0YWRhdGEgY29sdW1uIHRoYXQgY29ycmVzcG9uZHMKIyMjIHRvIHRoZSBjZWxsICMKYGBge3J9CmRmXzQwOCA9IGRhdGEuZnJhbWUoKQpmb3IgKGZpbGVfbmFtZSBpbiBsaXN0LmZpbGVzKGRhdGFfZGlyKSl7CiAgcHJpbnQoZmlsZV9uYW1lKQogIGlmKGdyZXBsKCcxNjQnLCBmaWxlX25hbWUpKXsKICAgIG5leHQKICB9CiAgI2lmKGdyZXBsKCc0MDhfVEMnLCBmaWxlX25hbWUpIHwgZ3JlcGwoJzQwOF92TVMnLCBmaWxlX25hbWUpKXsKICAjICBuZXh0CiAgI30KICBkZl90b19hcHBlbmQgPC0gcmVhZC50YWJsZShmaWxlLnBhdGgoZGF0YV9kaXIsIGZpbGVfbmFtZSksIHNlcCA9ICcsJywgaGVhZGVyID0gVFJVRSkKICB3aGlsZShsZW5ndGgoaW5kIDwtIHdoaWNoKGRmX3RvX2FwcGVuZCRJbWFnZS5OYW1lID09ICIiKSkgPiAwKXsKICAgIGRmX3RvX2FwcGVuZCRJbWFnZS5OYW1lW2luZF0gPC0gZGZfdG9fYXBwZW5kJEltYWdlLk5hbWVbaW5kIC0xXQogIH0KICAKICBjb2xuYW1lcyhkZl90b19hcHBlbmQpIDwtIHRvdXBwZXIoY29sbmFtZXMoZGZfdG9fYXBwZW5kKSkKICBkZl90b19hcHBlbmQgPC0gZGZfdG9fYXBwZW5kICU+JQogICAgbXV0YXRlKGFyZWEgPSBzdHJzcGxpdChmaWxlX25hbWUsICcuY3N2JylbWzFdXSkKICAKICAjIyBBZGQgcmVsYXRpdmVfWFlfcG9zaXRpb24KICAKICBpZighaXNfZW1wdHkoZGZfNDA4KSl7CiAgICBkZl90b19hcHBlbmQgPC0gZGZfdG9fYXBwZW5kICU+JQogICAgICAgICAgZHBseXI6OnNlbGVjdChjb2xuYW1lcyhkZl80MDgpKQogIH0KICBkZl80MDggPC0gcmJpbmQoZGZfNDA4LCBkZl90b19hcHBlbmQpCn0KYGBgCmBgYHtyfQpkZl80MDgkSU1BR0UuTkFNRSA9IHVubGlzdChsYXBwbHkoZGZfNDA4JElNQUdFLk5BTUUsIGdzdWIsIHBhdHRlcm49J19DbHVzdGVyJywgcmVwbGFjZW1lbnQ9JycpKQpkZl80MDgkSU1BR0UuTkFNRSA9IHVubGlzdChsYXBwbHkoZGZfNDA4JElNQUdFLk5BTUUsIGdzdWIsIHBhdHRlcm49J1sqXScsIHJlcGxhY2VtZW50PScnKSkKZGZfNDA4JElNQUdFLk5BTUUgPSB1bmxpc3QobGFwcGx5KGRmXzQwOCRJTUFHRS5OQU1FLCBnc3ViLCBwYXR0ZXJuPSdYJywgcmVwbGFjZW1lbnQ9JycpKQpkZl80MDgkSU1BR0UuTkFNRSA9IHVubGlzdChsYXBwbHkoZGZfNDA4JElNQUdFLk5BTUUsIGdzdWIsIHBhdHRlcm49J0wyXycsIHJlcGxhY2VtZW50PSdMMi0nKSkKZGZfNDA4JElNQUdFLk5BTUUgPSB1bmxpc3QobGFwcGx5KGRmXzQwOCRJTUFHRS5OQU1FLCBnc3ViLCBwYXR0ZXJuPSctTDInLCByZXBsYWNlbWVudD0nX0wyJykpCmRmXzQwOCRJTUFHRS5OQU1FID0gdW5saXN0KGxhcHBseShkZl80MDgkSU1BR0UuTkFNRSwgZ3N1YiwgcGF0dGVybj0nVGNfMTInLCByZXBsYWNlbWVudD0nVENfMTInKSkKIyMgTWlzc2luZwpkZl80MDggPSBkZl80MDhbZGZfNDA4JElNQUdFLk5BTUUgIT0gJ0xheWVyMScsIF0KZGZfNDA4ID0gZGZfNDA4W2RmXzQwOCRJTUFHRS5OQU1FICE9ICdUQ18xJywgXQpkZl80MDggPSBkZl80MDhbZGZfNDA4JElNQUdFLk5BTUUgIT0gJ1RDXzE4JywgXQpkZl80MDggPSBkZl80MDhbZGZfNDA4JElNQUdFLk5BTUUgIT0gJ1RDXzE5JywgXQojZGZfNDA4JElNQUdFLk5BTUUgPSB0b3VwcGVyKGRmXzQwOCRJTUFHRS5OQU1FKQp1bmlxdWUoZGZfNDA4JElNQUdFLk5BTUUpCmBgYAojIyBOb3cgd2Uga25vdyB0aGF0IGV2ZXJ5dGhpbmcgaXMgZXF1YWwgdG8gb25lIGFub3RoZXIsIHdlIHNob3VsZCBsb2FkIHRoZSB2YXJpYWJsZQoKYGBge3J9CmltYWdlX25hbWVzID0gdW5pcXVlKGRmXzQwOCRJTUFHRS5OQU1FKQojIFByZXNldCB0aGVzZSB2YXJpYWJsZXMgdG8gbmVnYXRpdmUgdmFsdWVzIHNvIEkgY2FuIGVhc2lseSBjaGVjayBpZiB0aGV5IHdlcmUgdXBkYXRlZCBsYXRlcgpkZl80MDgkWCA9IC0xCmRmXzQwOCRZID0gLTEKIyBzZXQgc29tZSBub3JtYWxpemF0aW9uIHZhcmlhYmxlcwojIyBUaGlzIGlzIHRoZSBzaXplIG9mIHRoZSBpbWFnZSB3aGVuIHRoZSBwaXhlbCB2YWx1ZXMgYXJlIHRha2VuIGZyb20gdG9wIGxlZnQgZG93bgpJTUFHRV9TSVpFID0gMTAyNAojIyBUaGlzIGlzIHRoZSBzaXplIG9mIGFuIGltYWdlIGluIHRoZSBnbG9iYWwgY29vcmRpbmF0ZSBzcGFjZQpJTUFHRV9MRU4gPSAyMAoKIyBMb2FkIHRoZSBkYXRhZnJhbWUgd2l0aCBnbG9iYWwgYW5kIHJlbGF0aXZlIGNvb3JkaW5hdGVzCmltZ19jb3JkcyA9IHJlYWQudGFibGUoZmlsZS5wYXRoKG1ldGFfZGlyLCAnNDA4X3BpeGVsX2Nvb3JkaW5hdGVzLmNzdicpLCBzZXAgPSAnLCcsIGhlYWRlciA9IFRSVUUpCgppbWFnZXMgPSBsaXN0LmZpbGVzKG1ldGFfZGlyKQpmb3IoaW1hZ2VfbmFtZSBpbiBpbWFnZV9uYW1lcyl7CiAgICAgIGlmKGdyZXBsKCcxNjQnLCBpbWFnZV9uYW1lKSl7CiAgICAgIG5leHQKICAgIH0KICAgIHNwbGl0X25hbWVzID0gc3Ryc3BsaXQoaW1hZ2VfbmFtZSwgJ18nKQogICAgY29ydGV4ID0gdG91cHBlcihzcGxpdF9uYW1lc1tbMV1dWzFdKQogICAgbnVtYmVyID0gc3BsaXRfbmFtZXNbWzFdXVsyXQogICAgbnVtYmVyX2NzdiA9IHBhc3RlMCgnXycsIG51bWJlciwgJy5jc3YnKQogICAgZmlsZW5hbWUgPSBpbWFnZXNbZ3JlcGwoY29ydGV4LCBpbWFnZXMpICYgZ3JlcGwobnVtYmVyX2NzdiwgaW1hZ2VzKSAmIGdyZXBsKCc0MDgnLCBpbWFnZXMpXQogICAgY29vcmRpbmF0ZXMgPSByZWFkLnRhYmxlKGZpbGUucGF0aChtZXRhX2RpciwgZmlsZW5hbWUpLCBzZXAgPSAnLCcsIGhlYWRlciA9IFRSVUUpCiAgICAjIyBjaGVja2VkIGFscmVhZHkgdGhhdCBsaXN0cyBhcmUgZXF1YWwsIG1pc3NpbmcgMSwgMTgsIDE5IGZvciBub3csIGxheWVyIDEgYW5kIG90aGVycwogICAgaWYoY29ydGV4ID09ICdDQycpeyAKICAgICAgI3ByaW50KHBhc3RlKCdjYycsIGZpbGVuYW1lLCBpbWFnZV9uYW1lKSkKICAgICAgeF9hZGogPSAwCiAgICAgIHlfYWRqID0gMAogICAgfSBlbHNlIGlmKGFzLm51bWVyaWMobnVtYmVyKSA8PSAxMCl7CiAgICAgICNwcmludChwYXN0ZSgndGM8MTEnLCBmaWxlbmFtZSwgaW1hZ2VfbmFtZSkpCiAgICAgIHhfYWRqID0gMTgwI2ltZ19jb3Jkc1tpbWdfY29yZHMkTmFtZSA9PSAnR19UQ18xJywgJ3gnXQogICAgICB5X2FkaiA9IDIxMCAjaW1nX2NvcmRzW2ltZ19jb3JkcyROYW1lID09ICdHX1RDXzEnLCAneSddCiAgICB9ZWxzZXsKICAgICAgI3ByaW50KHBhc3RlKCd0Yz49MTEnLCBmaWxlbmFtZSwgaW1hZ2VfbmFtZSkpCiAgICAgIHhfYWRqID0gMzgwI2ltZ19jb3Jkc1tpbWdfY29yZHMkTmFtZSA9PSAnR19UQ18xMScsICd4J10KICAgICAgeV9hZGogPSA0MTAgI2ltZ19jb3Jkc1tpbWdfY29yZHMkTmFtZSA9PSAnR19UQ18xMScsICd5J10KICAgIH0KICAgIAogICAgIyMgc28gdGhpcyBpcyBhIGxpdHRsZSB0cmlja3ksIHNvIG5lZWQgdG8gZ2V0IGl0IHJpZ2h0CiAgICAjIyBSZW1lbWJlciwgaXQgaXMgdGhlIHRvcCByaWdodCB0aGF0IHRoZSBjb29yZGluYXRlIGlzIGNvbWluZyBmcm9tLCBidXQKICAgICMjIHRoZSBib3R0b20gcmlnaHQgaXMgdGhlIG5ldyBjb29yZGluYXRlIHNwYWNlLgogICAgIyMgc28gZmlyc3Qgd2hlbiB3ZSBnZXQgdGhlIG9yaWdpbmFsIGNvb3JkaW5hdGUgc3BhY2UsIHRvIHNldCB0byByZWxhdGl2ZQogICAgIyMgb2YgYm90dG9tIHdvdWxkIGJlIHRoZSBzYW1lIFgsIGJ1dCAxMDI0IC0gWQogICAgCiAgICAjIyBwdXNoIG91dCB0aGUgY29vcmRpbmF0ZXMgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uCiAgICB4X3JlcGVsbGVkIDwtICg1MTIgLSBjb29yZGluYXRlcyRYX0Nvb3JkaW5hdGVfSW5fcGl4ZWxzKQogICAgCiAgICAKICAgIGRmXzQwOFtkZl80MDgkSU1BR0UuTkFNRSA9PSBpbWFnZV9uYW1lLCAnWCddID0gKHhfcmVwZWxsZWQgLyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSU1BR0VfU0laRSAqIElNQUdFX0xFTikgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltZ19jb3Jkc1tpbWdfY29yZHMkTmFtZSA9PSBpbWFnZV9uYW1lLCAneCddICsgeF9hZGoKICAgIGRmXzQwOFtkZl80MDgkSU1BR0UuTkFNRSA9PSBpbWFnZV9uYW1lLCAnWSddID0gKCgxMDI0LWNvb3JkaW5hdGVzJFlfQ29vcmRpbmF0ZV9Jbl9waXhlbHMpIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElNQUdFX1NJWkUgKiBJTUFHRV9MRU4pICsgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbWdfY29yZHNbaW1nX2NvcmRzJE5hbWUgPT0gaW1hZ2VfbmFtZSwgJ3knXSArIHlfYWRqICAgIAp9CmBgYAoKIyMgV2UgaGF2ZSB0aGUgY29vcmRpbmF0ZXMgZm9yIDQwOF9UQyBhbmQgb3RoZXJzCmBgYHtyfQpqeV80MDggPSBkZl80MDggJT4lCiAgZHBseXI6OnNlbGVjdCgtYyhhcmVhLCBJTUFHRS5OQU1FLCBYLCBZKSkgJT4lCiAgdCgpICU+JQogIENyZWF0ZVNldXJhdE9iamVjdCgpCmBgYAoKIyMganVzdCBzZXQgZXZlcnl0aGluZyBmcm9tIGJlbG93IDEgaW4gcmF0aW8gdG8gemVybwpgYGB7cn0KanlfNDA4IDwtIE5vcm1hbGl6ZURhdGEoanlfNDA4LCBzY2FsZS5mYWN0b3IgPSAxZTUpICMjIwpub3JtZWQgPSBHZXRBc3NheURhdGEoanlfNDA4LCBzbG90ID0gJ2RhdGEnKQpub3JtZWRbbm9ybWVkIDwgM10gPSAwCmp5XzQwOCA8LSBTZXRBc3NheURhdGEoanlfNDA4LCBzbG90ID0gJ2RhdGEnLCBub3JtZWQpCmBgYAoKYGBge3J9Cnh5Y29yZHMgPSBkZl80MDggJT4lIHNlbGVjdChjKCdYJywgJ1knKSkgJT4lIGFzLm1hdHJpeCgpCmNvbG5hbWVzKHh5Y29yZHMpIDwtIGMoJ3BpeGVsXzEnLCAncGl4ZWxfMicpCgpqeV80MDhbWyJYWSJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0geHljb3Jkcywga2V5ID0gInBpeGVsXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KGp5XzQwOCkpCmBgYAoKYGBge3J9Cmp5XzQwOCRnYWQxX3RydWUgPSBub3JtZWRbJ0dBRDEnLF0gIT0gMCAmIG5vcm1lZFsnU0FUQjInLF0gPT0gMApgYGAKCmBgYHtyfQpua3gyMSA8LSBub3JtZWRbJ05LWDIuMScsXSAhPSAwICYgbm9ybWVkWydMSFg2JyxdID09IDAgJiBqeV80MDgkZ2FkMV90cnVlIApsaHg2IDwtIG5vcm1lZFsnTktYMi4xJyxdID09IDAgJiBub3JtZWRbJ0xIWDYnLF0gIT0gMCAmIGp5XzQwOCRnYWQxX3RydWUgCm1nZV9ib3RoIDwtIG5vcm1lZFsnTktYMi4xJyxdICE9IDAgJiBub3JtZWRbJ0xIWDYnLF0gIT0gMCAmIGp5XzQwOCRnYWQxX3RydWUgCm1nZV9uZWl0aGVyIDwtIG5vcm1lZFsnTktYMi4xJyxdID09IDAgJiBub3JtZWRbJ0xIWDYnLF0gPT0gMCAmIGp5XzQwOCRnYWQxX3RydWUgCm5vbl9pbnRlcm5ldXJvbiA8LSAhanlfNDA4JGdhZDFfdHJ1ZSAKCmp5XzQwOCRtZ2VfbGluZWFnZSA9ICdlcnJvcicKanlfNDA4JG1nZV9saW5lYWdlW25reDIxXSA9ICdOS1gyLjEnCmp5XzQwOCRtZ2VfbGluZWFnZVtsaHg2XSA9ICdMSFg2JwpqeV80MDgkbWdlX2xpbmVhZ2VbbWdlX2JvdGhdID0gJ05LWDIuMSAmIExIWDYnCmp5XzQwOCRtZ2VfbGluZWFnZVttZ2VfbmVpdGhlcl0gPSAnTm9uLU1HRScKanlfNDA4JG1nZV9saW5lYWdlW25vbl9pbnRlcm5ldXJvbl0gPSAnTm9uLUlOJwpgYGAKCgpgYGB7cn0KRGltUGxvdChqeV80MDgsICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgY29scyA9IGMoJ2dyZXknLCAncHVycGxlJyksIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAwLjIsIGdyb3VwLmJ5ID0gJ2dhZDFfdHJ1ZScsIG9yZGVyID0gd2hpY2goanlfNDA4JGdhZDFfdHJ1ZSkpCmBgYApgYGB7cn0KRGltUGxvdChqeV80MDgsICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgI2NvbHMgPSBjKCdncmV5JywgJ3B1cnBsZScsICdibHVlJywgJ3JlZCcsICdob3RwaW5rMScpLCAKICAgICAgICByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMC4xLCBzcGxpdC5ieSA9ICdtZ2VfbGluZWFnZScsIAogICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGxpc3Qobmt4Mi4xID0gd2hpY2gobmt4MjEpLCBsaHg2ID0gd2hpY2gobGh4NiksIGJvdGggPSB3aGljaChtZ2VfYm90aCkpLAogICAgICAgIGNvbHMuaGlnaGxpZ2h0ID0gYygnb3JhbmdlMScsJ2hvdHBpbmsxJywgJ2JsYWNrJyksIAogICAgICAgIG9yZGVyID0gd2hpY2goanlfNDA4JGdhZDFfdHJ1ZSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSArIE5vQXhlcygpCmBgYAoKCmBgYHtyfQpzcDggPC0gbm9ybWVkWydTUDgnLF0gIT0gMCAmIG5vcm1lZFsnQ09VUFRGMicsXSA9PSAwICYganlfNDA4JGdhZDFfdHJ1ZSAKY291cHRmMiA8LSBub3JtZWRbJ1NQOCcsXSA9PSAwICYgbm9ybWVkWydDT1VQVEYyJyxdICE9IDAgJiBqeV80MDgkZ2FkMV90cnVlIApjZ2VfbGdlIDwtIG5vcm1lZFsnU1A4JyxdICE9IDAgJiBub3JtZWRbJ0NPVVBURjInLF0gIT0gMCAmIGp5XzQwOCRnYWQxX3RydWUgCm5laXRoZXJfY2dlX2xnZSA8LSBub3JtZWRbJ1NQOCcsXSA9PSAwICYgbm9ybWVkWydDT1VQVEYyJyxdID09IDAgJiBqeV80MDgkZ2FkMV90cnVlIApub25faW50ZXJuZXVyb24gPC0gIWp5XzQwOCRnYWQxX3RydWUgCgpqeV80MDgkY2dlX2xpbmVhZ2UgPSAnZXJyb3InCmp5XzQwOCRjZ2VfbGluZWFnZVtzcDhdID0gJ1NQOCcKanlfNDA4JGNnZV9saW5lYWdlW2NvdXB0ZjJdID0gJ0NPVVBURjInCmp5XzQwOCRjZ2VfbGluZWFnZVtjZ2VfbGdlXSA9ICdTUDggJiBDT1VQVEYyJwpqeV80MDgkY2dlX2xpbmVhZ2VbbmVpdGhlcl9jZ2VfbGdlXSA9ICdOb24tQ0dFL0xHRScKanlfNDA4JGNnZV9saW5lYWdlW25vbl9pbnRlcm5ldXJvbl0gPSAnTm9uLUlOJwpgYGAKCmBgYHtyfQpEaW1QbG90KGp5XzQwOCwgI2NlbGxzID0gZ3JlcGwoJ0NDJywgZGZfNDA4JGFyZWEpLCAKICAgICAgICAjY29scyA9IGMoJ2dyZXknLCAncHVycGxlJywgJ2JsdWUnLCAncmVkJywgJ2hvdHBpbmsxJyksIAogICAgICAgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAxLCBzcGxpdC5ieSA9ICdjZ2VfbGluZWFnZScsIAogICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGxpc3Qoc3A4ID0gd2hpY2goc3A4KSwgY291cHRmMiA9IHdoaWNoKGNvdXB0ZjIpLCBib3RoID0gd2hpY2goY2dlX2xnZSkpLAogICAgICAgIGNvbHMuaGlnaGxpZ2h0ID0gYygncmVkJywnYmx1ZScsICdwdXJwbGUnKSwgCiAgICAgICAgb3JkZXIgPSB3aGljaChqeV80MDgkZ2FkMV90cnVlKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpICsgTm9BeGVzKCkKYGBgCgpgYGB7cn0KanlfNDA4IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGp5XzQwOCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiKQphbGwuZ2VuZXMgPC0gcm93bmFtZXMoanlfNDA4KQpqeV80MDggPC0gU2NhbGVEYXRhKGp5XzQwOCwgZmVhdHVyZXMgPSBhbGwuZ2VuZXMpCmp5XzQwOCA8LSBSdW5QQ0EoanlfNDA4LCBhcHByb3ggPSBGQUxTRSkKanlfNDA4IDwtIEZpbmROZWlnaGJvcnMoanlfNDA4LCBkaW1zID0gMTozMCkKanlfNDA4IDwtIEZpbmRDbHVzdGVycyhqeV80MDgsIHJlc29sdXRpb24gPSAwLjgpCmp5XzQwOCA8LSBSdW5VTUFQKGp5XzQwOCwgZGltcyA9IDE6MzApCgpEaW1QbG90KGp5XzQwOCwgY29scyA9IGMoJ2dyZWVueWVsbG93JywgJ2xpZ2h0Z29sZGVucm9keWVsbG93JywgJ29yYW5nZTEnLCAnYXF1YW1hcmluZTQnLCAndG9tYXRvJywgJ2RvZGdlcmJsdWUzJywgJ3Zpb2xldHJlZDMnKSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdzZXVyYXRfY2x1c3RlcnMnKSArIE5vQXhlcygpCmBgYAoKCmBgYHtyfQpEaW1QbG90KGp5XzQwOCwgI2NlbGxzID0gZ3JlcGwoJ0NDJywgZGZfNDA4JGFyZWEpLCAKICAgICAgICAjY29scyA9IGMoJ2dyZWVueWVsbG93JywgJ2xpZ2h0Z29sZGVucm9keWVsbG93JywgJ29yYW5nZTEnLCAnYXF1YW1hcmluZTQnLCAndG9tYXRvJywgJ2RvZGdlcmJsdWUzJywgJ3Zpb2xldHJlZDMnKSwgCiAgICAgICAgY29scyA9IGMoJ2dyZWVueWVsbG93JywgJ2xpZ2h0Z29sZGVucm9keWVsbG93JywgJ2FxdWFtYXJpbmU0JywgJ29yYW5nZTEnLCAnZG9kZ2VyYmx1ZTMnLCAndG9tYXRvJywgJ3Zpb2xldHJlZDMnKSwgCiAgICAgICAgcmVkdWN0aW9uID0gIlhZIiwgb3JkZXIgPSBjKDYsIDQsIDMsIDIsIDAsIDEsIDUpLCBwdC5zaXplID0gMSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpICNncm91cC5ieSA9ICdtZ2VfbGluZWFnZScsIAogICAgICAgICNjZWxscy5oaWdobGlnaHQgPSBsaXN0KHNwOCA9IHdoaWNoKHNwOCksIGNvdXB0ZjIgPSB3aGljaChjb3VwdGYyKSwgYm90aCA9IHdoaWNoKGNnZV9sZ2UpKSwKICAgICAgICAjY29scy5oaWdobGlnaHQgPSBjKCdyZWQnLCdibHVlJywgJ3B1cnBsZScpLCAKICAgICAgICAjb3JkZXIgPSB3aGljaChqeV80MDgkZ2FkMV90cnVlKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpCmBgYAoKYGBge3J9CmNsdXN0ZXJfYnlfbG9jID0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY2JpbmQocm93bmFtZXMoZGZfNDA4KSwganlfNDA4JHNldXJhdF9jbHVzdGVycywgZGZfNDA4JFgsIGRmXzQwOCRZKSkpCmNvbG5hbWVzKGNsdXN0ZXJfYnlfbG9jKSA9IGMoJ2NlbGxudW0nLCAnY2x1c3RlcicsICdYJywgJ1knKQpgYGAKCmBgYHtyfQpjbHVzdGVyX2J5X2xvYyAlPiUKICAjZmlsdGVyKGNsdXN0ZXIgPT0gMykgJT4lCiAgZ2dwbG90KGFlcyh4ID0gWCwgY29sb3IgPSBjbHVzdGVyKSkgKyBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICdpZGVudGl0eScsIGJpbnMgPSA1MCwgYmlud2lkdGggPSAxMDAwLCBzdGF0ID0gJ2NvdW50JykKYGBgCmBgYHtyfQpEaW1QbG90KGp5XzQwOCwgI2NlbGxzID0gZ3JlcGwoJ0NDJywgZGZfNDA4JGFyZWEpLCAKICAgICAgICAjY29scyA9IGMoJ2dyZXknLCAncHVycGxlJywgJ2JsdWUnLCAncmVkJywgJ2hvdHBpbmsxJyksIAogICAgICAgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAxLCAjZ3JvdXAuYnkgPSAnbWdlX2xpbmVhZ2UnLCAKICAgICAgICBjZWxscy5oaWdobGlnaHQgPSBsaXN0KGNsdXN0ZXIwID0gd2hpY2goanlfNDA4JHNldXJhdF9jbHVzdGVycyA9PSAwKSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSMsCiAgICAgICAgI2NvbHMuaGlnaGxpZ2h0ID0gYygncmVkJywnYmx1ZScsICdwdXJwbGUnKSwgCiAgICAgICAgI29yZGVyID0gd2hpY2goanlfNDA4JGdhZDFfdHJ1ZSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKQpgYGAKCmBgYHtyfQpEaW1QbG90KGp5XzQwOCwgI2NlbGxzID0gZ3JlcGwoJ0NDJywgZGZfNDA4JGFyZWEpLCAKICAgICAgICAjY29scyA9IGMoJ2dyZXknLCAncHVycGxlJywgJ2JsdWUnLCAncmVkJywgJ2hvdHBpbmsxJyksIAogICAgICAgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAxLCAjZ3JvdXAuYnkgPSAnbWdlX2xpbmVhZ2UnLCAKICAgICAgICBjZWxscy5oaWdobGlnaHQgPSBsaXN0KGNsdXN0ZXIxID0gd2hpY2goanlfNDA4JHNldXJhdF9jbHVzdGVycyA9PSAxKSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSMsCmBgYAoKYGBge3J9CkRpbVBsb3QoanlfNDA4LCAjY2VsbHMgPSBncmVwbCgnQ0MnLCBkZl80MDgkYXJlYSksIAogICAgICAgICNjb2xzID0gYygnZ3JleScsICdwdXJwbGUnLCAnYmx1ZScsICdyZWQnLCAnaG90cGluazEnKSwgCiAgICAgICAgcmVkdWN0aW9uID0gIlhZIiwgcHQuc2l6ZSA9IDEsICNncm91cC5ieSA9ICdtZ2VfbGluZWFnZScsIAogICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGxpc3QoY2x1c3RlcjIgPSB3aGljaChqeV80MDgkc2V1cmF0X2NsdXN0ZXJzID09IDIpKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpIywKYGBgCgpgYGB7cn0KRGltUGxvdChqeV80MDgsICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgI2NvbHMgPSBjKCdncmV5JywgJ3B1cnBsZScsICdibHVlJywgJ3JlZCcsICdob3RwaW5rMScpLCAKICAgICAgICByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMSwgI2dyb3VwLmJ5ID0gJ21nZV9saW5lYWdlJywgCiAgICAgICAgY2VsbHMuaGlnaGxpZ2h0ID0gbGlzdChjbHVzdGVyMyA9IHdoaWNoKGp5XzQwOCRzZXVyYXRfY2x1c3RlcnMgPT0gMykpKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9yZXZlcnNlKCkjLApgYGAKCmBgYHtyfQpEaW1QbG90KGp5XzQwOCwgI2NlbGxzID0gZ3JlcGwoJ0NDJywgZGZfNDA4JGFyZWEpLCAKICAgICAgICAjY29scyA9IGMoJ2dyZXknLCAncHVycGxlJywgJ2JsdWUnLCAncmVkJywgJ2hvdHBpbmsxJyksIAogICAgICAgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAxLCAjZ3JvdXAuYnkgPSAnbWdlX2xpbmVhZ2UnLCAKICAgICAgICBjZWxscy5oaWdobGlnaHQgPSBsaXN0KGNsdXN0ZXI0ID0gd2hpY2goanlfNDA4JHNldXJhdF9jbHVzdGVycyA9PSA0KSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSMsCmBgYAoKYGBge3J9CkRpbVBsb3QoanlfNDA4LCAjY2VsbHMgPSBncmVwbCgnQ0MnLCBkZl80MDgkYXJlYSksIAogICAgICAgICNjb2xzID0gYygnZ3JleScsICdwdXJwbGUnLCAnYmx1ZScsICdyZWQnLCAnaG90cGluazEnKSwgCiAgICAgICAgcmVkdWN0aW9uID0gIlhZIiwgcHQuc2l6ZSA9IDEsICNncm91cC5ieSA9ICdtZ2VfbGluZWFnZScsIAogICAgICAgIGNlbGxzLmhpZ2hsaWdodCA9IGxpc3QoY2x1c3RlcjUgPSB3aGljaChqeV80MDgkc2V1cmF0X2NsdXN0ZXJzID09IDUpKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpIywKYGBgCgpgYGB7cn0KRGltUGxvdChqeV80MDgsICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgI2NvbHMgPSBjKCdncmV5JywgJ3B1cnBsZScsICdibHVlJywgJ3JlZCcsICdob3RwaW5rMScpLCAKICAgICAgICByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMSwgI2dyb3VwLmJ5ID0gJ21nZV9saW5lYWdlJywgCiAgICAgICAgY2VsbHMuaGlnaGxpZ2h0ID0gbGlzdChjbHVzdGVyNiA9IHdoaWNoKGp5XzQwOCRzZXVyYXRfY2x1c3RlcnMgPT0gNikpKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9yZXZlcnNlKCkjLApgYGAKCmBgYHtyfQpicnVoIDwtIEZlYXR1cmVQbG90KGp5XzQwOCwgZmVhdHVyZXMgPSBjKCdOS1gyLjEnLCAnTEhYNicpLAogICAgICAgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAxLCBvcmRlciA9IFRSVUUsIHNwbGl0LmJ5ID0gJ2FyZWEnLCBieS5jb2wgPSBUUlVFKSAjKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpICsgTm9BeGVzKCkgKyBOb0xlZ2VuZCgpCmBgYApgYGB7cn0KRGltUGxvdChqeV80MDgsICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgY29scyA9IGMoJ2dyZWVueWVsbG93JywgJ2xpZ2h0Z29sZGVucm9keWVsbG93JywgJ29yYW5nZTEnLCAnYXF1YW1hcmluZTQnLCAndG9tYXRvJywgJ2RvZGdlcmJsdWUzJywgJ3Zpb2xldHJlZDMnKSwKICAgICAgICByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMSwgc3BsaXQuYnkgPSAnc2V1cmF0X2NsdXN0ZXJzJykgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpICsgTm9BeGVzKCkgKyBOb0xlZ2VuZCgpCmBgYApgYGB7cn0KanlfNDA4Lm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoanlfNDA4LCBvbmx5LnBvcyA9IFRSVUUsIG1pbi5wY3QgPSAwLjI1LCBsb2dmYy50aHJlc2hvbGQgPSAwLjI1KQpqeV80MDgubWFya2VycyAlPiUKICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgIHNsaWNlX21heChuID0gMzIsIG9yZGVyX2J5ID0gYXZnX2xvZzJGQykKYGBgCiMjIFNBVEIyIG5ldXJvbnMgcmVtb3ZlZAoKYGBge3J9Cmp5XzQwOF9JTiA8LSBqeV80MDhbLCBqeV80MDgkZ2FkMV90cnVlXQpqeV80MDhfSU4gPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoanlfNDA4X0lOLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIpCmFsbC5nZW5lcyA8LSByb3duYW1lcyhqeV80MDhfSU4pCmp5XzQwOF9JTiA8LSBTY2FsZURhdGEoanlfNDA4X0lOLCBmZWF0dXJlcyA9IGFsbC5nZW5lcykKanlfNDA4X0lOIDwtIFJ1blBDQShqeV80MDhfSU4sIGFwcHJveCA9IEZBTFNFKQpqeV80MDhfSU4gPC0gRmluZE5laWdoYm9ycyhqeV80MDhfSU4sIGRpbXMgPSAxOjMwKQpqeV80MDhfSU4gPC0gRmluZENsdXN0ZXJzKGp5XzQwOF9JTiwgcmVzb2x1dGlvbiA9IDAuOCkKanlfNDA4X0lOIDwtIFJ1blVNQVAoanlfNDA4X0lOLCBkaW1zID0gMTozMCkKCkRpbVBsb3QoanlfNDA4X0lOLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ3NldXJhdF9jbHVzdGVycycpCmBgYApgYGB7cn0KanlfNDA4X0lOW1siWFkiXV0gPC0gQ3JlYXRlRGltUmVkdWNPYmplY3QoZW1iZWRkaW5ncyA9IHh5Y29yZHNbanlfNDA4JGdhZDFfdHJ1ZSwgXSwga2V5ID0gInBpeGVsXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KGp5XzQwOF9JTikpCmBgYAoKYGBge3J9CkRpbVBsb3QoanlfNDA4X0lOLCAjY2VsbHMgPSBncmVwbCgnQ0MnLCBkZl80MDgkYXJlYSksIAogICAgICAgICNjb2xzID0gYygnZ3JleScsICdwdXJwbGUnLCAnYmx1ZScsICdyZWQnLCAnaG90cGluazEnKSwgCiAgICAgICAgcmVkdWN0aW9uID0gIlhZIiwgcHQuc2l6ZSA9IDEpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSAKYGBgCmBgYHtyfQpqeV80MDhfSU4ubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhqeV80MDhfSU4sIG9ubHkucG9zID0gVFJVRSwgbWluLnBjdCA9IDAuMjUsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUpCmp5XzQwOF9JTi5tYXJrZXJzICU+JQogICBncm91cF9ieShjbHVzdGVyKSAlPiUKICAgc2xpY2VfbWF4KG4gPSAzMiwgb3JkZXJfYnkgPSBhdmdfbG9nMkZDKQpgYGAKIyMgTm8gU0FUQjIgbmV1cm9ucyByZW1vdmVkCgpgYGB7cn0KanlfNDA4X0lOIDwtIGp5XzQwOFssIGp5XzQwOCRnYWQxX3RydWVdCmp5XzQwOF9JTiA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhqeV80MDhfSU4sIHNlbGVjdGlvbi5tZXRob2QgPSAidnN0IikKYWxsLmdlbmVzIDwtIHJvd25hbWVzKGp5XzQwOF9JTikKanlfNDA4X0lOIDwtIFNjYWxlRGF0YShqeV80MDhfSU4sIGZlYXR1cmVzID0gYWxsLmdlbmVzKQpqeV80MDhfSU4gPC0gUnVuUENBKGp5XzQwOF9JTiwgYXBwcm94ID0gRkFMU0UpCmp5XzQwOF9JTiA8LSBGaW5kTmVpZ2hib3JzKGp5XzQwOF9JTiwgZGltcyA9IDE6MzApCmp5XzQwOF9JTiA8LSBGaW5kQ2x1c3RlcnMoanlfNDA4X0lOLCByZXNvbHV0aW9uID0gMC44KQpqeV80MDhfSU4gPC0gUnVuVU1BUChqeV80MDhfSU4sIGRpbXMgPSAxOjMwKQoKRGltUGxvdChqeV80MDhfSU4sIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnc2V1cmF0X2NsdXN0ZXJzJykKYGBgCmBgYHtyfQpqeV80MDhfSU5bWyJYWSJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0geHljb3Jkc1tqeV80MDgkZ2FkMV90cnVlLCBdLCBrZXkgPSAicGl4ZWxfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoanlfNDA4X0lOKSkKYGBgCgpgYGB7cn0KRGltUGxvdChqeV80MDhfSU4sICNjZWxscyA9IGdyZXBsKCdDQycsIGRmXzQwOCRhcmVhKSwgCiAgICAgICAgI2NvbHMgPSBjKCdncmV5JywgJ3B1cnBsZScsICdibHVlJywgJ3JlZCcsICdob3RwaW5rMScpLCAKICAgICAgICByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpIApgYGAKCgoKYGBge3J9Cmp5XzQwOF9JTi5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGp5XzQwOF9JTiwgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgbG9nZmMudGhyZXNob2xkID0gMC4yNSkKanlfNDA4X0lOLm1hcmtlcnMgJT4lCiAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogICBzbGljZV9tYXgobiA9IDMyLCBvcmRlcl9ieSA9IGF2Z19sb2cyRkMpCmBgYAoKIyMgQWRqdXN0IHRvIGdldCB0aGUgcmlnaHQgb3ZlcmxheQoKYGBge3J9CmltYWdlX25hbWVzID0gdW5pcXVlKGRmXzQwOCRJTUFHRS5OQU1FKQojIFByZXNldCB0aGVzZSB2YXJpYWJsZXMgdG8gbmVnYXRpdmUgdmFsdWVzIHNvIEkgY2FuIGVhc2lseSBjaGVjayBpZiB0aGV5IHdlcmUgdXBkYXRlZCBsYXRlcgpkZl80MDgkWCA9IC0xCmRmXzQwOCRZID0gLTEKIyBzZXQgc29tZSBub3JtYWxpemF0aW9uIHZhcmlhYmxlcwojIyBUaGlzIGlzIHRoZSBzaXplIG9mIHRoZSBpbWFnZSB3aGVuIHRoZSBwaXhlbCB2YWx1ZXMgYXJlIHRha2VuIGZyb20gdG9wIGxlZnQgZG93bgpJTUFHRV9TSVpFID0gMTAyNAojIyBUaGlzIGlzIHRoZSBzaXplIG9mIGFuIGltYWdlIGluIHRoZSBnbG9iYWwgY29vcmRpbmF0ZSBzcGFjZQpJTUFHRV9MRU4gPSAyMAoKIyBMb2FkIHRoZSBkYXRhZnJhbWUgd2l0aCBnbG9iYWwgYW5kIHJlbGF0aXZlIGNvb3JkaW5hdGVzCmltZ19jb3JkcyA9IHJlYWQudGFibGUoZmlsZS5wYXRoKG1ldGFfZGlyLCAnNDA4X3BpeGVsX2Nvb3JkaW5hdGVzLmNzdicpLCBzZXAgPSAnLCcsIGhlYWRlciA9IFRSVUUpCgppbWFnZXMgPSBsaXN0LmZpbGVzKG1ldGFfZGlyKQpmb3IoaW1hZ2VfbmFtZSBpbiBpbWFnZV9uYW1lcyl7CiAgICBpZihncmVwbCgnMTY0JywgaW1hZ2VfbmFtZSkpewogICAgICBuZXh0CiAgICB9CiAgICBzcGxpdF9uYW1lcyA9IHN0cnNwbGl0KGltYWdlX25hbWUsICdfJykKICAgIGNvcnRleCA9IHRvdXBwZXIoc3BsaXRfbmFtZXNbWzFdXVsxXSkKICAgIG51bWJlciA9IHNwbGl0X25hbWVzW1sxXV1bMl0KICAgIG51bWJlcl9jc3YgPSBwYXN0ZTAoJ18nLCBudW1iZXIsICcuY3N2JykKICAgIGZpbGVuYW1lID0gaW1hZ2VzW2dyZXBsKGNvcnRleCwgaW1hZ2VzKSAmIGdyZXBsKG51bWJlcl9jc3YsIGltYWdlcyldCiAgICBjb29yZGluYXRlcyA9IHJlYWQudGFibGUoZmlsZS5wYXRoKG1ldGFfZGlyLCBmaWxlbmFtZSksIHNlcCA9ICcsJywgaGVhZGVyID0gVFJVRSkKICAgICMjIGNoZWNrZWQgYWxyZWFkeSB0aGF0IGxpc3RzIGFyZSBlcXVhbCwgbWlzc2luZyAxLCAxOCwgMTkgZm9yIG5vdywgbGF5ZXIgMSBhbmQgb3RoZXJzCiAgICBpZihjb3J0ZXggPT0gJ0NDJyl7IAogICAgICAjcHJpbnQocGFzdGUoJ2NjJywgZmlsZW5hbWUsIGltYWdlX25hbWUpKQogICAgICB4X2FkaiA9IDIwCiAgICAgIHlfYWRqID0gMTg4CiAgICB9IGVsc2UgaWYoYXMubnVtZXJpYyhudW1iZXIpIDw9IDEwKXsKICAgICAgI3ByaW50KHBhc3RlKCd0YzwxMScsIGZpbGVuYW1lLCBpbWFnZV9uYW1lKSkKICAgICAgeF9hZGogPSA0MTAjaW1nX2NvcmRzW2ltZ19jb3JkcyROYW1lID09ICdHX1RDXzEnLCAneCddCiAgICAgIHlfYWRqID0gNDcwICNpbWdfY29yZHNbaW1nX2NvcmRzJE5hbWUgPT0gJ0dfVENfMScsICd5J10KICAgIH1lbHNlewogICAgICAjcHJpbnQocGFzdGUoJ3RjPj0xMScsIGZpbGVuYW1lLCBpbWFnZV9uYW1lKSkKICAgICAgeF9hZGogPSA1OTAjaW1nX2NvcmRzW2ltZ19jb3JkcyROYW1lID09ICdHX1RDXzExJywgJ3gnXQogICAgICB5X2FkaiA9IDc5MCAjaW1nX2NvcmRzW2ltZ19jb3JkcyROYW1lID09ICdHX1RDXzExJywgJ3knXQogICAgfQogICAgCiAgICAjIyBzbyB0aGlzIGlzIGEgbGl0dGxlIHRyaWNreSwgc28gbmVlZCB0byBnZXQgaXQgcmlnaHQKICAgICMjIFJlbWVtYmVyLCBpdCBpcyB0aGUgdG9wIHJpZ2h0IHRoYXQgdGhlIGNvb3JkaW5hdGUgaXMgY29taW5nIGZyb20sIGJ1dAogICAgIyMgdGhlIGJvdHRvbSByaWdodCBpcyB0aGUgbmV3IGNvb3JkaW5hdGUgc3BhY2UuCiAgICAjIyBzbyBmaXJzdCB3aGVuIHdlIGdldCB0aGUgb3JpZ2luYWwgY29vcmRpbmF0ZSBzcGFjZSwgdG8gc2V0IHRvIHJlbGF0aXZlCiAgICAjIyBvZiBib3R0b20gd291bGQgYmUgdGhlIHNhbWUgWCwgYnV0IDEwMjQgLSBZCiAgICAKICAgICMjIHB1c2ggb3V0IHRoZSBjb29yZGluYXRlcyBmb3IgYmV0dGVyIHZpc3VhbGl6YXRpb24KICAgICN4X3JlcGVsbGVkIDwtICg1MTIgLSBjb29yZGluYXRlcyRYX0Nvb3JkaW5hdGVfSW5fcGl4ZWxzKQogICAgCiAgICAKICAgIGRmXzQwOFtkZl80MDgkSU1BR0UuTkFNRSA9PSBpbWFnZV9uYW1lLCAnWCddID0gKGNvb3JkaW5hdGVzJFhfQ29vcmRpbmF0ZV9Jbl9waXhlbHMgLyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSU1BR0VfU0laRSAqIElNQUdFX0xFTikgKyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGltZ19jb3Jkc1tpbWdfY29yZHMkTmFtZSA9PSBpbWFnZV9uYW1lLCAneCddLzEuNSArIHhfYWRqCiAgICBkZl80MDhbZGZfNDA4JElNQUdFLk5BTUUgPT0gaW1hZ2VfbmFtZSwgJ1knXSA9ICgoMTAyNC1jb29yZGluYXRlcyRZX0Nvb3JkaW5hdGVfSW5fcGl4ZWxzKSAvIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJTUFHRV9TSVpFICogSU1BR0VfTEVOKSArIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW1nX2NvcmRzW2ltZ19jb3JkcyROYW1lID09IGltYWdlX25hbWUsICd5J10gKyB5X2FkaiAgICAKfQoKeHljb3JkcyA9IGRmXzQwOCAlPiUgc2VsZWN0KGMoJ1gnLCAnWScpKSAlPiUgYXMubWF0cml4KCkKY29sbmFtZXMoeHljb3JkcykgPC0gYygncGl4ZWxfMScsICdwaXhlbF8yJykKCmp5XzQwOFtbIlhZIl1dIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSB4eWNvcmRzLCBrZXkgPSAicGl4ZWxfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoanlfNDA4KSkKYGBgCgoKYGBge3J9CiNodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85OTE3MDQ5L2luc2VydGluZy1hbi1pbWFnZS10by1nZ3Bsb3QyCgp0aGVtZV9zZXQodGhlbWVfY293cGxvdCgpKQoKYmFkX2NvbG9ycyA8LSBEaW1QbG90KGp5XzQwOCwgY29scyA9IGMoJ2dyZWVueWVsbG93JywgJ2xpZ2h0Z29sZGVucm9keWVsbG93JywgJ2FxdWFtYXJpbmU0JywgJ29yYW5nZTEnLCAnZG9kZ2VyYmx1ZTMnLCAndG9tYXRvJywgJ3Zpb2xldHJlZDMnKSwgIHJlZHVjdGlvbiA9ICJYWSIsIHB0LnNpemUgPSAwLjAxLCBvcmRlciA9IGMoNiwgNCwgMywgMiwgMCwgMSwgNSkpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSArIHhsaW0oODUyLCAwKSArIHlsaW0oMTI0MiwgMCkgKyBOb0xlZ2VuZCgpICsgY29vcmRfZml4ZWQoKSArIE5vQXhlcygpCgojeG9yaWcgPSAtODUyCiN5b3JpZyA9IC0xMjQyCgpnZ2RyYXcoKSArCiAgZHJhd19pbWFnZSgnfi9zdC9hcmNfcHJvZmlsaW5nL3N0X2FuYWx5c2lzL2hhbmRfYW5ub3RhdGVkX2RhdGEvaW1hZ2VzLzQwOF9zbGljZV9ub2JveGVzX25vY29sb3IucG5nJywKICAgICAgICAgICAgIHggPSAwLCB5ID0gMCkgKwogIGRyYXdfcGxvdChiYWRfY29sb3JzKQpgYGAKYGBge3J9CkRpbVBsb3QoanlfNDA4LCByZWR1Y3Rpb24gPSAiWFkiLCBwdC5zaXplID0gMC4xKSsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSAgICsgeGxpbSg4NTIsIDApICsgeWxpbSgxMjQyLCAwKSAjICsgTm9MZWdlbmQoKSArIE5vQXhlcygpCmBgYApgYGB7cn0KanlfNDA4X3NwIDwtIGRmXzQwOCAlPiUKICBkcGx5cjo6c2VsZWN0KC1jKGFyZWEsIElNQUdFLk5BTUUpKSAlPiUKICB0KCkgJT4lCiAgQ3JlYXRlU2V1cmF0T2JqZWN0KCkKanlfNDA4X3NwIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGp5XzQwOF9zcCwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiKQphbGwuZ2VuZXMgPC0gcm93bmFtZXMoanlfNDA4KQpqeV80MDhfc3AgPC0gU2NhbGVEYXRhKGp5XzQwOF9zcCwgZmVhdHVyZXMgPSBhbGwuZ2VuZXMpCmp5XzQwOF9zcCA8LSBSdW5QQ0EoanlfNDA4X3NwLCBhcHByb3ggPSBGQUxTRSkKanlfNDA4X3NwIDwtIEZpbmROZWlnaGJvcnMoanlfNDA4X3NwLCBkaW1zID0gMTozMCkKanlfNDA4X3NwIDwtIEZpbmRDbHVzdGVycyhqeV80MDhfc3AsIHJlc29sdXRpb24gPSAwLjgpCmp5XzQwOF9zcCA8LSBSdW5VTUFQKGp5XzQwOF9zcCwgZGltcyA9IDE6MzApCgpEaW1QbG90KGp5XzQwOF9zcCwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdzZXVyYXRfY2x1c3RlcnMnKQpgYGAKYGBge3J9Cmp5XzQwOF9zcFtbIlhZIl1dIDwtIENyZWF0ZURpbVJlZHVjT2JqZWN0KGVtYmVkZGluZ3MgPSB4eWNvcmRzLCBrZXkgPSAicGl4ZWxfIiwgYXNzYXkgPSBEZWZhdWx0QXNzYXkoanlfNDA4X3NwKSkKYGBgCgpgYGB7cn0KRGltUGxvdChqeV80MDhfc3AsIHJlZHVjdGlvbiA9ICJYWSIsIHNwbGl0LmJ5ID0gJ3NldXJhdF9jbHVzdGVycycpCmBgYAoKCmBgYHtyfQpGZWF0dXJlUGxvdChqeV80MDhfc3AsIGMoJ1gnLCAnWScpKQpgYGAKCmBgYHtyfQpyZWxuIDwtIG5vcm1lZFsnVkxETFInLF0gIT0gMCAmIG5vcm1lZFsnTFJQOCcsXSA9PSAwICYganlfNDA4JGdhZDFfdHJ1ZSAKbHJwOCA8LSBub3JtZWRbJ1ZMRExSJyxdID09IDAgJiBub3JtZWRbJ0xSUDgnLF0gIT0gMCAmIGp5XzQwOCRnYWQxX3RydWUgCnJlbG5ib3RoIDwtIG5vcm1lZFsnVkxETFInLF0gIT0gMCAmIG5vcm1lZFsnTFJQOCcsXSAhPSAwICYganlfNDA4JGdhZDFfdHJ1ZSAKcmVsbm5laXRoZXIgPC0gbm9ybWVkWydWTERMUicsXSA9PSAwICYgbm9ybWVkWydMUlA4JyxdID09IDAgJiBqeV80MDgkZ2FkMV90cnVlIApub25faW50ZXJuZXVyb24gPC0gIWp5XzQwOCRnYWQxX3RydWUgCgpqeV80MDgkcmVsbl9zaWduYWxpbmcgPSAnZXJyb3InCmp5XzQwOCRyZWxuX3NpZ25hbGluZ1tyZWxuXSA9ICdWTERMUicKanlfNDA4JHJlbG5fc2lnbmFsaW5nW2xycDhdID0gJ0xSUDgnCmp5XzQwOCRyZWxuX3NpZ25hbGluZ1tyZWxuYm90aF0gPSAnVkxETFIgJiBMUlA4JwpqeV80MDgkcmVsbl9zaWduYWxpbmdbcmVsbm5laXRoZXJdID0gJ05laXRoZXInCmp5XzQwOCRyZWxuX3NpZ25hbGluZ1tub25faW50ZXJuZXVyb25dID0gJ05vbi1JTicKYGBgCgoKYGBge3J9CiMjIGp1c3QgZGVmaW5lIHNldHMgb2YgY2VsbHMgdGhhdCBJIHdhbnQgdG8gcGxvdApjaW5ndWxhdGVfTVMgPSBncmVwbCgnQ0MnLCBkZl80MDgkSU1BR0UuTkFNRSkgCmRvcnNhbF9UQ19NUyA9IGRmXzQwOCRJTUFHRS5OQU1FICVpbiUgb3V0ZXIoJ1RDXycsIDI6MTAsIEZVTj1wYXN0ZTApCnZlbnRyYWxfVENfTVMgPSAhY2luZ3VsYXRlX01TICYgIWRvcnNhbF9UQ19NUwpgYGAKCmBgYHtyfQpGZWF0dXJlUGxvdChqeV80MDgsIGNlbGxzID0gd2hpY2godmVudHJhbF9UQ19NUyksIHJlZHVjdGlvbiA9ICJYWSIsIGZlYXR1cmVzID0gJ1RCUjEnKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9yZXZlcnNlKCkgCmBgYApgYGB7cn0KRmVhdHVyZVBsb3QoanlfNDA4LCBjZWxscyA9IHdoaWNoKHZlbnRyYWxfVENfTVMpLCByZWR1Y3Rpb24gPSAiWFkiLCBmZWF0dXJlcyA9ICdMUlA4JykgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpIApgYGAKCgpgYGB7cn0KRmVhdHVyZVBsb3QoanlfNDA4LCBjZWxscyA9IHdoaWNoKGRvcnNhbF9UQ19NUyksIHJlZHVjdGlvbiA9ICJYWSIsIGZlYXR1cmVzID0gYygnQ1hDUjQnLCAnQ0FMQjInKSwgb3JkZXIgPSBUUlVFKSArIHNjYWxlX3lfcmV2ZXJzZSgpICsgc2NhbGVfeF9yZXZlcnNlKCkgCmBgYApgYGB7cn0KRmVhdHVyZVBsb3QoanlfNDA4LCBjZWxscyA9IHdoaWNoKHZlbnRyYWxfVENfTVMpLCByZWR1Y3Rpb24gPSAiWFkiLCBmZWF0dXJlcyA9IGMoJ0NYQ1I0JywgJ0NBTEIyJyksIG9yZGVyID0gVFJVRSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpIApgYGAKYGBge3J9CkZlYXR1cmVQbG90KGp5XzQwOCwgY2VsbHMgPSB3aGljaChjaW5ndWxhdGVfTVMpLCByZWR1Y3Rpb24gPSAiWFkiLCBmZWF0dXJlcyA9IGMoJ05LWDIuMScpLCBvcmRlciA9IFRSVUUpICsgc2NhbGVfeV9yZXZlcnNlKCkgKyBzY2FsZV94X3JldmVyc2UoKSAKYGBgCgoKYGBge3J9CkZlYXR1cmVQbG90KGp5XzQwOCwgY2VsbHMgPSB3aGljaCh2ZW50cmFsX1RDX01TKSwgcmVkdWN0aW9uID0gIlhZIiwgZmVhdHVyZXMgPSBjKCdDWENSNCcsICdDQUxCMicpLCBvcmRlciA9IFRSVUUpICsKICBzdGF0X2RlbnNpdHlfMmQoYWVzKGZpbGwgPSAuLmRlbnNpdHkuLiksIGdlb20gPSAicmFzdGVyIiwgY29udG91ciA9IEZBTFNFKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZT00LCBkaXJlY3Rpb249LTEpIApgYGAKCmBgYHtyfQpub3JtZWRfZGF0YSA9IHQoYXMubWF0cml4KEdldEFzc2F5RGF0YShqeV80MDgsIHNsb3QgPSAnZGF0YScpKSkgCm5vcm1fYm9vbHMgPSBub3JtZWRfZGF0YSA+IDAKCmRlbnNpdHlfZGYgPSBhcy5kYXRhLmZyYW1lKGNiaW5kKHh5Y29yZHMsIG5vcm1fYm9vbHMsIGp5XzQwOCRzZXVyYXRfY2x1c3RlcnMpKQpjb2xuYW1lcyhkZW5zaXR5X2RmKSA9IGMoJ1gnLCAnWScsIGNvbG5hbWVzKG5vcm1fYm9vbHMpLCAnY2x1c3RlcicpCmRlbnNpdHlfZGYkZm92ID0gJ2Vycm9yJwpkZW5zaXR5X2RmJGZvdltkb3JzYWxfVENfTVNdID0gJ2RNU19UQycKZGVuc2l0eV9kZiRmb3ZbdmVudHJhbF9UQ19NU10gPSAndk1TX1RDJwpkZW5zaXR5X2RmJGZvdltjaW5ndWxhdGVfTVNdID0gJ01TX0NDJwpgYGAKCmBgYHtyfQpkZW5zaXR5X2RmICU+JQogIGdncGxvdChhZXMoeCA9IFgsIHkgPSBZKSkgICsKICBnZW9tX2JpbjJkKGJpbnMgPSAyMCkgKwogIHNjYWxlX2ZpbGxfY29udGludW91cyh0eXBlID0gInZpcmlkaXMiKSArCiAgdGhlbWVfYncoKQoKYGBgCgoKYGBge3J9CkRpbVBsb3QoanlfNDA4LCAjY2VsbHMgPSBncmVwbCgnQ0MnLCBkZl80MDgkYXJlYSksIAogICAgICAgICNjb2xzID0gYygnZ3JleScsICdwdXJwbGUnLCAnYmx1ZScsICdyZWQnLCAnaG90cGluazEnKSwgCiAgICAgICAgcmVkdWN0aW9uID0gIlhZIiwgcHQuc2l6ZSA9IDAuMSwgc3BsaXQuYnkgPSAncmVsbl9zaWduYWxpbmcnLCAKICAgICAgICBjZWxscy5oaWdobGlnaHQgPSBsaXN0KHZsZGxyID0gd2hpY2gocmVsbiksIGxycDggPSB3aGljaChscnA4KSwgYm90aCA9IHdoaWNoKHJlbG5ib3RoKSksCiAgICAgICAgY29scy5oaWdobGlnaHQgPSBjKCdvcmFuZ2UxJywnaG90cGluazEnLCAnYmxhY2snKSwgCiAgICAgICAgb3JkZXIgPSB3aGljaChqeV80MDgkZ2FkMV90cnVlKSkgKyBzY2FsZV95X3JldmVyc2UoKSArIHNjYWxlX3hfcmV2ZXJzZSgpCmBgYAoKCiMjIExFdHMgZG8gdGhlIGZ1bmN0aW9uYWwgZ3JhcGhzCmBgYHtyfQpnb2lfcHJpbWFyeSA9IGMoJ1ZJUCcsICdTU1QnLCAnQ1hDUjQnKQpnb2kgPSBjKCdWSVAnLCAnU1NUJywgJ0NYQ1I0JywgJ0NYQ1I3JywgJ0NYQ0wxNCcsICdDWENMMTInLCAnTFJQOCcsICdWTERMUicsICdSRUxOJykKZ29pID0gYygnRENEQzInLCAnS0lBMDMxOScpCmBgYAoKIyMgTGV0J3MgY29tcGFyZSBTUDggdnMgQ09VUFRGMiBhdmVyYWdlIGV4cHJlc3Npb24gb2YgdGhlc2UgbWFya2VycwpgYGB7cn0KZnhuX2dlbmVzICA9IGFzLmRhdGEuZnJhbWUobm9ybWVkX2RhdGFbLCBnb2ldKQpmeG5fZ2VuZXMkY2dlID0ganlfNDA4JGNnZV9saW5lYWdlCmZ4bl9nZW5lcyRmb3YgPSBkZW5zaXR5X2RmJGZvdgoKZnhuX2dlbmVzICU+JQogIGZpbHRlcihmb3YgPT0gJ2RNU19UQycpICU+JQogIHBpdm90X2xvbmdlcighYyhjZ2UsIGZvdiksIG5hbWVzX3RvID0gImdlbmUiLCB2YWx1ZXNfdG8gPSAiZXhwciIpICU+JQogIGdncGxvdChhZXMoeCA9IGdlbmUsIHkgPSBleHByLCBmaWxsID0gY2dlKSkgKyBnZW9tX2JveHBsb3QoKSMgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKQpgYGAKCmBgYHtyfQpmeG5fZ2VuZXMgJT4lCiAgZmlsdGVyKGZvdiA9PSAndk1TX1RDJykgJT4lCiAgcGl2b3RfbG9uZ2VyKCFjKGNnZSwgZm92KSwgbmFtZXNfdG8gPSAiZ2VuZSIsIHZhbHVlc190byA9ICJleHByIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZ2VuZSwgeSA9IGV4cHIsIGZpbGwgPSBjZ2UpKSArIGdlb21fYm94cGxvdCgpIyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpCmBgYAoKYGBge3J9CmNmb3YgPSAnTVNfQ0MnCmZ4bl9nZW5lcyAlPiUKICBmaWx0ZXIoZm92ID09IGNmb3YpICU+JQogIHBpdm90X2xvbmdlcighYyhjZ2UsIGZvdiksIG5hbWVzX3RvID0gImdlbmUiLCB2YWx1ZXNfdG8gPSAiZXhwciIpICU+JQogIGdncGxvdChhZXMoeCA9IGdlbmUsIHkgPSBleHByLCBmaWxsID0gY2dlKSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZ3JlZW4iLCJncmF5IiwiZ3JheSIsImJsdWUiLCAicmVkIikpICsgZ2d0aXRsZShjZm92KQpgYGAKYGBge3J9CmNmb3YgPSAndk1TX1RDJwpmeG5fZ2VuZXMgJT4lCiAgZmlsdGVyKGZvdiA9PSBjZm92KSAlPiUKICBwaXZvdF9sb25nZXIoIWMoY2dlLCBmb3YpLCBuYW1lc190byA9ICJnZW5lIiwgdmFsdWVzX3RvID0gImV4cHIiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBnZW5lLCB5ID0gZXhwciwgZmlsbCA9IGNnZSkpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImdyZWVuIiwiZ3JheSIsImdyYXkiLCJibHVlIiwgInJlZCIpKSArIGdndGl0bGUoY2ZvdikKYGBgCgpgYGB7cn0KY2ZvdiA9ICdkTVNfVEMnCmZ4bl9nZW5lcyAlPiUKICBmaWx0ZXIoZm92ID09IGNmb3YpICU+JQogIHBpdm90X2xvbmdlcighYyhjZ2UsIGZvdiksIG5hbWVzX3RvID0gImdlbmUiLCB2YWx1ZXNfdG8gPSAiZXhwciIpICU+JQogIGdncGxvdChhZXMoeCA9IGdlbmUsIHkgPSBleHByLCBmaWxsID0gY2dlKSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZ3JlZW4iLCJncmF5IiwiZ3JheSIsImJsdWUiLCAicmVkIikpICsgZ2d0aXRsZShjZm92KQpgYGAKYGBge3J9CmZ4bl9nZW5lcyAgPSBhcy5kYXRhLmZyYW1lKG5vcm1fYm9vbHNbLCBnb2ldKQpmeG5fZ2VuZXMkY2dlID0ganlfNDA4JGNnZV9saW5lYWdlCmZ4bl9nZW5lcyRmb3YgPSBkZW5zaXR5X2RmJGZvdgoKZnhuX2dlbmVzICU+JQogIGZpbHRlcihmb3YgPT0gJ2RNU19UQycpICU+JQogIHBpdm90X2xvbmdlcighYyhjZ2UsIGZvdiksIG5hbWVzX3RvID0gImdlbmUiLCB2YWx1ZXNfdG8gPSAiZXhwciIpICU+JQogIGdncGxvdChhZXMoeCA9IGdlbmUsIHkgPSBhcy5udW1lcmljKGV4cHIpLCBmaWxsID0gY2dlKSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJzdW1tYXJ5IiwgZnVuID0gIm1lYW4iKSArCiAgeGxhYigicGN0IikgKyBnZ3RpdGxlKCdkTVNfVEMnKQpgYGAKCmBgYHtyfQpmeG5fZ2VuZXMgID0gYXMuZGF0YS5mcmFtZShub3JtX2Jvb2xzWywgZ29pXSkKZnhuX2dlbmVzJGNnZSA9IGp5XzQwOCRjZ2VfbGluZWFnZQpmeG5fZ2VuZXMkZm92ID0gZGVuc2l0eV9kZiRmb3YKCmNmb3YgPSAndk1TX1RDJwoKZnhuX2dlbmVzICU+JQogIGZpbHRlcihmb3YgPT0gY2ZvdikgJT4lCiAgc2VsZWN0KC1mb3YpICU+JQogIHBpdm90X2xvbmdlcighbWdlLCBuYW1lc190byA9ICJnZW5lIiwgdmFsdWVzX3RvID0gImV4cHIiKSAlPiUKICBkYXRhX3N1bW1hcnkodmFybmFtZT0iZXhwciIsIGdyb3VwbmFtZXM9YygiZ2VuZSIsICJtZ2UiKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWdlbmUsIHk9ZXhwciwgZmlsbD1tZ2UpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgY29sb3I9ImJsYWNrIiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkgICsgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1leHByLXNlbSwgeW1heD1leHByK3NlbSksIHdpZHRoPS4yLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSguOSkpICsKICB5bGFiKCJwY3QiKSArIGdndGl0bGUoY2ZvdikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiY2FkZXRibHVlMSIsImJyb3duMiIsImRhcmtzbGF0ZWJsdWUiLCJncmV5IiwgImtoYWtpMSIpKSArCiAgdGhlbWVfY2xhc3NpYygpCgojYygiY2FkZXRibHVlMSIsImJyb3duMiIsImRhcmtzbGF0ZWJsdWUiLCJncmV5IiwgImtoYWtpMSIpIGZvciBtZ2UKYGBgCgpgYGB7cn0KI2h0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS9nZ3Bsb3QyLWVycm9yLWJhcnMtcXVpY2stc3RhcnQtZ3VpZGUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uCmxpYnJhcnkocGxvdHJpeCkKZGF0YV9zdW1tYXJ5IDwtIGZ1bmN0aW9uKGRhdGEsIHZhcm5hbWUsIGdyb3VwbmFtZXMpewogIHJlcXVpcmUocGx5cikKICBzdW1tYXJ5X2Z1bmMgPC0gZnVuY3Rpb24oeCwgY29sKXsKICAgIGMobWVhbiA9IG1lYW4oeFtbY29sXV0sIG5hLnJtPVRSVUUpLAogICAgICBzZW0gPSBzdGQuZXJyb3IoeFtbY29sXV0sIG5hLnJtPVRSVUUpKQogIH0KICBkYXRhX3N1bTwtZGRwbHkoZGF0YSwgZ3JvdXBuYW1lcywgLmZ1bj1zdW1tYXJ5X2Z1bmMsCiAgICAgICAgICAgICAgICAgIHZhcm5hbWUpCiAgZGF0YV9zdW0gPC0gcmVuYW1lKGRhdGFfc3VtLCBjKCJtZWFuIiA9IHZhcm5hbWUpKQogcmV0dXJuKGRhdGFfc3VtKQp9CmBgYApgYGB7cn0KZGYyIDwtIGZ4bl9nZW5lcyAlPiUKICBzZWxlY3QoLWZvdikgJT4lCiAgcGl2b3RfbG9uZ2VyKCFjZ2UsIG5hbWVzX3RvID0gImdlbmUiLCB2YWx1ZXNfdG8gPSAiZXhwciIpICU+JQogIGRhdGFfc3VtbWFyeSh2YXJuYW1lPSJleHByIiwgCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBuYW1lcz1jKCJnZW5lIiwgImNnZSIpKQpgYGAKCmBgYHtyfQogZ2dwbG90KGRmMiwgYWVzKHg9Z2VuZSwgeT1leHByLCBmaWxsPWNnZSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBjb2xvcj0iYmxhY2siLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1leHByLXNlbSwgeW1heD1leHByK3NlbSksIHdpZHRoPS4yLAogICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKC45KSkgCmBgYAoKYGBge3J9CiBnZ3Bsb3QoZGYyLCBhZXMoeD1nZW5lLCB5PWV4cHIsIGZpbGw9Y2dlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWV4cHItc2VtLCB5bWF4PWV4cHIrc2VtKSwgd2lkdGg9LjIsCiAgICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoLjkpKSAKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3QoanlfNDA4X0lOLCByZWR1Y3Rpb24gPSAnWFknLCBmZWF0dXJlcyA9IGMoJ0RDREMyJywgJ0tJQTAzMTknKSwgb3JkZXIgPSBUUlVFKQpgYGAKYGBge3J9CmZ4bl9nZW5lcyAlPiUKICBzZWxlY3QoLWMoZm92LCBtZ2UpKSAlPiUKICBwaXZvdF9sb25nZXIoIWNnZSwgbmFtZXNfdG8gPSAiZ2VuZSIsIHZhbHVlc190byA9ICJleHByIikgJT4lCiAgZGF0YV9zdW1tYXJ5KHZhcm5hbWU9ImV4cHIiLCBncm91cG5hbWVzPWMoImdlbmUiLCAiY2dlIikpICU+JQogIGdncGxvdChhZXMoeD1nZW5lLCB5PWV4cHIsIGZpbGw9Y2dlKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpICArIGdlb21fZXJyb3JiYXIoYWVzKHltaW49ZXhwci1zZW0sIHltYXg9ZXhwcitzZW0pLCB3aWR0aD0uMiwgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoLjkpKSArCiAgeWxhYigicGN0IikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiZGFya29saXZlZ3JlZW4xIiwib3JhbmdlMiIsInJvc3licm93bjQiLCJncmF5OTYiLCAiZ3JleSIpKSArCiAgdGhlbWVfY2xhc3NpYygpCmBgYApgYGB7cn0Kc3VtKGZ4bl9nZW5lcyRjZ2UgPT0gJ1NQOCAmIENPVVBURjInKQpzdW0oanlfNDA4JGdhZDFfdHJ1ZSkKCnByaW50KDIwNi8zODgpCmBgYAoKIyMgT3JkZXIgdGhlIGltYWdlcwpgYGB7cn0KdW5pcXVlKGRmXzQwOCRJTUFHRS5OQU1FKQppbWFnZXNfb3JkZXJlZCA9IGMoJ1RDXzIwJywgJ1RDXzE3JywgJ1RDXzE2JywgJ1RDXzE1JywgJ1RDXzE0JywgJ1RDXzEzJywgJ1RDXzEyJywgJ1RDXzExJywKICAgICAgICAgICAnVENfMTAnLCAnVENfOScsICdUQ184JywgJ1RDXzcnLCAnVENfNicsICdUQ180JywgJ1RDXzQnLCAnVENfMycsICdUQ18yJywKICAgICAgICAgICAnQ0NfNCcsICdDQ181JywgJ0NDXzYnLCAnQ0NfNicsICdDQ184JywgJ0NDXzknLCAnQ0NfMTAnLCAnQ0NfMTEnLCAnQ0NfMTInLAogICAgICAgICAgICdDQ19MMi0xJywgJ0NDX0wyLTInLCAnQ0NfTDItMycsICdDQ19Db3J0aWNhbDEnLCAnQ0NfQ29ydGljYWwyJykKYGBgCgpgYGB7cn0KeF9ob3J6ID0gMTpsZW5ndGgoaW1hZ2VzX29yZGVyZWQpICogMjUKeV9ob3J6ID0gcmVwKDAsIGxlbmd0aChpbWFnZXNfb3JkZXJlZCkpCmhvcnpfZW1iZWRkaW5nID0gZGF0YS5mcmFtZSgpCmRmXzQwOCRYX2hvcnogPSAtMQpkZl80MDgkWV9ob3J6ID0gLTEKCgppbWFnZXMgPSBsaXN0LmZpbGVzKG1ldGFfZGlyKQpmb3IoaSBpbiAxOmxlbmd0aChpbWFnZXNfb3JkZXJlZCkpewogICAgaW1hZ2VfbmFtZSA9IGltYWdlc19vcmRlcmVkW2ldCiAgICBzcGxpdF9uYW1lcyA9IHN0cnNwbGl0KGltYWdlX25hbWUsICdfJykKICAgIGNvcnRleCA9IHRvdXBwZXIoc3BsaXRfbmFtZXNbWzFdXVsxXSkKICAgIG51bWJlciA9IHNwbGl0X25hbWVzW1sxXV1bMl0KICAgIG51bWJlcl9jc3YgPSBwYXN0ZTAoJ18nLCBudW1iZXIsICcuY3N2JykKICAgIGZpbGVuYW1lID0gaW1hZ2VzW2dyZXBsKGNvcnRleCwgaW1hZ2VzKSAmIGdyZXBsKG51bWJlcl9jc3YsIGltYWdlcyldCiAgICBjb29yZGluYXRlcyA9IHJlYWQudGFibGUoZmlsZS5wYXRoKG1ldGFfZGlyLCBmaWxlbmFtZSksIHNlcCA9ICcsJywgaGVhZGVyID0gVFJVRSkKICAgICMjIGNoZWNrZWQgYWxyZWFkeSB0aGF0IGxpc3RzIGFyZSBlcXVhbCwgbWlzc2luZyAxLCAxOCwgMTkgZm9yIG5vdywgbGF5ZXIgMSBhbmQgb3RoZXJzCiAKICAgICMjIHNvIHRoaXMgaXMgYSBsaXR0bGUgdHJpY2t5LCBzbyBuZWVkIHRvIGdldCBpdCByaWdodAogICAgIyMgUmVtZW1iZXIsIGl0IGlzIHRoZSB0b3AgcmlnaHQgdGhhdCB0aGUgY29vcmRpbmF0ZSBpcyBjb21pbmcgZnJvbSwgYnV0CiAgICAjIyB0aGUgYm90dG9tIHJpZ2h0IGlzIHRoZSBuZXcgY29vcmRpbmF0ZSBzcGFjZS4KICAgICMjIHNvIGZpcnN0IHdoZW4gd2UgZ2V0IHRoZSBvcmlnaW5hbCBjb29yZGluYXRlIHNwYWNlLCB0byBzZXQgdG8gcmVsYXRpdmUKICAgICMjIG9mIGJvdHRvbSB3b3VsZCBiZSB0aGUgc2FtZSBYLCBidXQgMTAyNCAtIFkKICAgIAogICAgIyMgcHVzaCBvdXQgdGhlIGNvb3JkaW5hdGVzIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbgogICAgI3hfcmVwZWxsZWQgPC0gKDUxMiAtIGNvb3JkaW5hdGVzJFhfQ29vcmRpbmF0ZV9Jbl9waXhlbHMpCiAgICAKICAgIAogICAgZGZfNDA4W2RmXzQwOCRJTUFHRS5OQU1FID09IGltYWdlX25hbWUsICdYX2hvcnonXSA9IChjb29yZGluYXRlcyRYX0Nvb3JkaW5hdGVfSW5fcGl4ZWxzIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElNQUdFX1NJWkUgKiBJTUFHRV9MRU4pICsgeV9ob3J6W2ldCiAgICBkZl80MDhbZGZfNDA4JElNQUdFLk5BTUUgPT0gaW1hZ2VfbmFtZSwgJ1lfaG9yeiddID0gKCgxMDI0LWNvb3JkaW5hdGVzJFlfQ29vcmRpbmF0ZV9Jbl9waXhlbHMpIC8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElNQUdFX1NJWkUgKiBJTUFHRV9MRU4pICsgeF9ob3J6W2ldCn0KYGBgCgpgYGB7cn0KaGNvb3JkcyA9IGRmXzQwOCAlPiUgZHBseXI6OnNlbGVjdChjKCdYX2hvcnonLCAnWV9ob3J6JykpICU+JSBhcy5tYXRyaXgoKQpjb2xuYW1lcyhoY29vcmRzKSA8LSBjKCdwaXhlbF8xJywgJ3BpeGVsXzInKQoKanlfNDA4W1siSCJdXSA8LSBDcmVhdGVEaW1SZWR1Y09iamVjdChlbWJlZGRpbmdzID0gaGNvb3Jkcywga2V5ID0gInBpeGVsXyIsIGFzc2F5ID0gRGVmYXVsdEFzc2F5KGp5XzQwOCkpCmBgYAoKYGBge3J9Cmp5XzQwOCRzYXRiMV90cnVlID0gIWp5XzQwOCRnYWQxX3RydWUKRGltUGxvdChqeV80MDgsIGNvbHMgPSBjKCdncmV5JywgJ3B1cnBsZScpLCByZWR1Y3Rpb24gPSAiSCIsIHB0LnNpemUgPSAwLjI1LCBncm91cC5ieSA9ICdnYWQxX3RydWUnLCBvcmRlciA9IHdoaWNoKGp5XzQwOCRnYWQxX3RydWUpKSArIGNvb3JkX2ZpeGVkKHJhdGlvID0gMC41KSArIE5vQXhlcygpICsgTm9MZWdlbmQoKQpgYGAKYGBge3J9CkRpbVBsb3QoanlfNDA4LCByZWR1Y3Rpb24gPSAiSCIsIHB0LnNpemUgPSAwLjI1LCBncm91cC5ieSA9ICdtZ2VfbGluZWFnZScsIG9yZGVyID0gIk5LWDIuMSAmIExIWDYiLCBjZWxscy5oaWdobGlnaHQgPSB3aGljaChqeV80MDgkbWdlX2xpbmVhZ2UgPT0gJ05LWDIuMSAmIExIWDYnKSwgY29scy5oaWdobGlnaHQgPSAncmVkJykgKyBjb29yZF9maXhlZChyYXRpbyA9IDEpICsgIE5vTGVnZW5kKCkgKyBOb0F4ZXMoKQpgYGAKCmBgYHtyfQp0aGVtZV9QdWJsaWNhdGlvbiA8LSBmdW5jdGlvbihiYXNlX3NpemU9MTQsIGJhc2VfZmFtaWx5PSJoZWx2ZXRpY2EiKSB7CiAgICAgIGxpYnJhcnkoZ3JpZCkKICAgICAgbGlicmFyeShnZ3RoZW1lcykKICAgICAgKHRoZW1lX2ZvdW5kYXRpb24oYmFzZV9zaXplPWJhc2Vfc2l6ZSwgYmFzZV9mYW1pbHk9YmFzZV9mYW1pbHkpCiAgICAgICArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gcmVsKDEuMiksIGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dCgpLAogICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BKSwKICAgICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BKSwKICAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9IE5BKSwKICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLHNpemUgPSByZWwoMSkpLAogICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoYW5nbGU9OTAsdmp1c3QgPTIpLAogICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSAtMC4yKSwKICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KCksIAogICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyPSJibGFjayIpLAogICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKCksCiAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyPSIjZjBmMGYwIiksCiAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gTkEpLAogICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICAgICAgICAgbGVnZW5kLmtleS5zaXplPSB1bml0KDAuMiwgImNtIiksCiAgICAgICAgICAgICAgIGxlZ2VuZC5tYXJnaW4gPSB1bml0KDAsICJjbSIpLAogICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiksCiAgICAgICAgICAgICAgIHBsb3QubWFyZ2luPXVuaXQoYygxMCw1LDUsNSksIm1tIiksCiAgICAgICAgICAgICAgIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGNvbG91cj0iI2YwZjBmMCIsZmlsbD0iI2YwZjBmMCIpLAogICAgICAgICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKQogICAgICAgICAgKSkKICAgICAgCn0KCnNjYWxlX2ZpbGxfUHVibGljYXRpb24gPC0gZnVuY3Rpb24oLi4uKXsKICAgICAgbGlicmFyeShzY2FsZXMpCiAgICAgIGRpc2NyZXRlX3NjYWxlKCJmaWxsIiwiUHVibGljYXRpb24iLG1hbnVhbF9wYWwodmFsdWVzID0gYygiIzM4NmNiMCIsIiNmZGI0NjIiLCIjN2ZjOTdmIiwiI2VmM2IyYyIsIiM2NjI1MDYiLCIjYTZjZWUzIiwiI2ZiOWE5OSIsIiM5ODRlYTMiLCIjZmZmZjMzIikpLCAuLi4pCgp9CgpzY2FsZV9jb2xvdXJfUHVibGljYXRpb24gPC0gZnVuY3Rpb24oLi4uKXsKICAgICAgbGlicmFyeShzY2FsZXMpCiAgICAgIGRpc2NyZXRlX3NjYWxlKCJjb2xvdXIiLCJQdWJsaWNhdGlvbiIsbWFudWFsX3BhbCh2YWx1ZXMgPSBjKCIjMzg2Y2IwIiwiI2ZkYjQ2MiIsIiM3ZmM5N2YiLCIjZWYzYjJjIiwiIzY2MjUwNiIsIiNhNmNlZTMiLCIjZmI5YTk5IiwiIzk4NGVhMyIsIiNmZmZmMzMiKSksIC4uLikKCn0KYGBgCgoKYGBge3J9CkZlYXR1cmVQbG90KGp5XzQwOCwgcHQuc2l6ZT0wLjEsIHJlZHVjdGlvbiA9ICJIIiwgZmVhdHVyZXMgPSAnTFJQOCcpICsgY29vcmRfZml4ZWQocmF0aW8gPSAwLjUpICsgIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBOb0xlZ2VuZCgpICsgTm9BeGVzKCkKYGBgCgo=